Выберите только первую строку повторяющегося значения в столбце 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)
Но это работает только тогда, когда идентификаторы являются последовательными. В случае пробелов между идентификаторами (удаленными) запрос прерывается. Как я могу это исправить?
Ответы
Ответ 1
Вы можете использовать полу-объединение 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
select min(id), Col1 from tableName group by Col1
Ответ 3
Если ваша RDBMS поддерживает функции Window Aggregate и/или функции 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
. Затем вы можете свернуть все идентификационные записи CurCol IS NULL
.
Если у вас нет возможности использовать агрегаты окон или функции LEAD/LAG, ваша задача немного сложнее.
Надеюсь, что это поможет.
Ответ 4
Так как 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
, что и текущая строка. Если нет совпадения справа, тогда следует выбрать текущую запись.
UPDATE
Для учета непоследовательных 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
текущая строка является верхней.
Ответ 5
как насчет этого простого подхода?
select distinct col1 from tbl