Выделите только первую строку повторяющегося значения в столбце в SQL - PullRequest
10 голосов
/ 31 декабря 2011

У меня есть таблица, в которой есть столбец, который может иметь одинаковые значения в пакете. Как это:

+----+---------+
| id |   Col1  | 
+----+---------+
| 1  | 6050000 |
+----+---------+
| 2  | 6050000 |
+----+---------+
| 3  | 6050000 |
+----+---------+
| 4  | 6060000 |
+----+---------+
| 5  | 6060000 |
+----+---------+
| 6  | 6060000 |
+----+---------+
| 7  | 6060000 |
+----+---------+
| 8  | 6060000 |
+----+---------+
| 9  | 6050000 |
+----+---------+
| 10 | 6000000 |
+----+---------+
| 11 | 6000000 |
+----+---------+

Теперь я хочу удалить строки, в которых значение Col1 повторяется, и выбрать только первое вхождение.
Для приведенной таблицы результат должен быть:

+----+---------+
| id |   Col1  | 
+----+---------+
| 1  | 6050000 |
+----+---------+
| 4  | 6060000 |
+----+---------+
| 9  | 6050000 |
+----+---------+
| 10 | 6000000 |
+----+---------+

Как я могу сделать это в SQL?
Обратите внимание, что следует удалять только строки пакета, а значения можно повторять в строках без пакета! id=1 & id=9 повторяются в результате выборки.

EDIT:
Я достиг этого, используя это:

select id,col1 from data as d1
where not exists (
    Select id from data as d2
    where d2.id=d1.id-1 and d1.col1=d2.col1 order by id limit 1)

Но это работает только тогда, когда идентификаторы являются последовательными. При наличии пробелов между идентификаторами (удаленными) запрос прерывается. Как я могу это исправить?

Ответы [ 4 ]

8 голосов
/ 31 декабря 2011

Вы можете использовать полусоединение EXISTS для идентификации кандидатов:

Выберите нужные строки:

SELECT * FROM tbl
WHERE NOT EXISTS (
    SELECT *
    FROM tbl t
    WHERE t.col1 = tbl.col1
    AND t.id = tbl.id - 1
    )
ORDER BY id

Избавьтесь от ненужных строк:

DELETE FROM tbl
-- SELECT * FROM tbl
WHERE EXISTS (
    SELECT *
    FROM   tbl t
    WHERE  t.col1 = tbl.col1
    AND    t.id   = tbl.id - 1
    )

Это эффективно удаляет каждую строку, где предыдущая строка имеет одинаковое значение в col1, тем самым достигая поставленной цели: выживает только первая строка каждого пакета.

Я оставил прокомментированный оператор SELECT, потому что вы должны всегда проверять, что будет удалено, прежде чем вы сделаете это.


Решение для непоследовательных идентификаторов:

Если ваша СУБД поддерживает оконные функции CTE и (например, PostgreSQL, Oracle, SQL Server, ... но не SQLite, MS Access или MySQL ), есть элегантный способ:

WITH x AS (
    SELECT *, row_number() OVER (ORDER BY id) AS rn
    FROM tbl
    )
SELECT id, col1
FROM   x
WHERE NOT EXISTS (
    SELECT *
    FROM   x x1
    WHERE  x1.col1 = x.col1
    AND    x1.rn   = x.rn - 1
    )
ORDER BY id;

Существует также не очень элегантный способ сделать работу без этих тонкостей .
Должно работать на вас:

SELECT id, col1
FROM   tbl
WHERE (
    SELECT t.col1 = tbl.col1
    FROM   tbl AS t
    WHERE  t.id < tbl.id
    ORDER  BY id DESC
    LIMIT  1) IS NOT TRUE
ORDER BY id

Инструмент для непоследовательных идентификаторов в тестовом корпусе

(протестировано в PostgreSQL)

CREATE TEMP TABLE tbl (id int, col1 int);
INSERT INTO tbl VALUES
 (1,6050000),(2,6050000),(6,6050000)
,(14,6060000),(15,6060000),(16,6060000)
,(17,6060000),(18,6060000),(19,6050000)
,(20,6000000),(111,6000000);
2 голосов
/ 31 декабря 2011

Если ваша СУБД поддерживает функции агрегирования окон и / или функции LEAD () и LAG (), вы можете использовать их для выполнения того, о чем вы пытаетесь сообщить.Следующий SQL поможет вам начать правильный путь:

SELECT id
     , Col AS CurCol
     , MAX(Col)
       OVER(ORDER BY id ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS PrevCol
     , MIN(COL)
       OVER(ORDER BY id ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) AS NextCol
FROM MyTable

Оттуда вы можете поместить этот SQL в производную таблицу с некоторой логикой CASE, которая, если NextCol или PrevCol одинаковакак CurCol, затем установите CurCol = NULL.Затем вы можете свернуть все записи id CurCol IS NULL.

Если у вас нет возможности использовать агрегаты окон или функции LEAD / LAG, ваша задача немного сложнее.

Надеюсь, это поможет.

2 голосов
/ 31 декабря 2011
select min(id), Col1 from tableName group by Col1 
1 голос
/ 31 декабря 2011

Поскольку id всегда последовательный, без пробелов и повторений, согласно вашему комментарию, вы можете использовать следующий метод:

SELECT t1.*
FROM atable t1
  LEFT JOIN atable t2 ON t1.id = t2.id + 1 AND t1.Col1 = t2.Col1
WHERE t2.id IS NULL

Таблица (внешняя-) соединена с собой напри условии, что id на левой стороне больше, чем на правой стороне, а их значения Col1 идентичны.Другими словами, условие: 'предыдущая строка содержит то же значение Col1, что и текущая строка' .Если справа нет совпадений, то следует выбрать текущую запись.


ОБНОВЛЕНИЕ

Для учета непоследовательных id с (чтооднако предполагается, что они уникальны и определяют порядок изменений Col1), вы также можете попробовать выполнить следующий запрос:

SELECT t1.*
FROM atable t1
  LEFT JOIN atable t2 ON t1.id > t2.id
  LEFT JOIN atable t3 ON t1.id > t3.id AND t3.id > t2.id
WHERE t3.id IS NULL
  AND (t2.id IS NULL OR t2.Col1 <> t1.Col1)

Третье самосоединение существует, чтобы убедиться, что второевозвращает строку, непосредственно предшествующую строке t1.То есть, если нет совпадения для t3, то либо t2 содержит предыдущую строку, либо совпадение отсутствует, последнее означает, что текущая строка t1 является верхней.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...