Выберите только первую строку повторяющегося значения в столбце SQL

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

+----+---------+
| 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 ответов


можно использовать EXISTS semi-join для идентификации кандидатов:

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

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,... но!--28-->не 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

для теста-кожух non-последовательные идентификаторы

(протестировано в 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);

select min(id), Col1 from tableName group by Col1 

если ваша СУБД поддерживает агрегатные функции окна и / или функции 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 в производную таблицу с некоторой логикой случая, если NextCol или PrevCol это то же самое, что CurCol затем установить CurCol = NULL. Затем вы можете свернуть устранить все записи id CurCol IS NULL.

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

надеюсь, что это помогает.


С 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 значение как текущая строка'. Если справа нет совпадения, то текущая запись должна быть выбрана.


обновление

для учета непересекающихся ids (которые, однако, считаются уникальными и определяют порядок изменений 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текущая строка является верхней.