Выберите только первую строку повторяющегося значения в столбце 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);
если ваша СУБД поддерживает агрегатные функции окна и / или функции 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
значение как текущая строка'. Если справа нет совпадения, то текущая запись должна быть выбрана.
обновление
для учета непересекающихся id
s (которые, однако, считаются уникальными и определяют порядок изменений 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
текущая строка является верхней.