我的表中有一列可能在突发中具有相同的值。像这样:
+----+---------+
| 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
。
编辑:
我用它实现了它:
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)
但这只适用于ID是顺序的。由于ID(已删除的)之间存在间隙,因此查询中断。我该如何解决这个问题?
答案 0 :(得分:8)
您可以使用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
语句,因为您应该始终检查在执行契约之前要删除的内容。
如果你的RDBMS支持CTE和window functions(比如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);
答案 1 :(得分:2)
select min(id), Col1 from tableName group by Col1
答案 2 :(得分:2)
如果您的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
。然后你可以折叠消除所有id记录CurCol IS NULL
。
如果您无法使用窗口聚合或LEAD / LAG功能,那么您的任务就会复杂一些。
希望这有帮助。
答案 3 :(得分:1)
由于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
比右侧的大{1}并且它们的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
的当前行是最前一行。