我有一个带有标识字段的表。从该表中获取所有已删除记录的 ID 的最佳 SQL 查询是什么?
6 回答
一种完全不同的方法是:
SELECT a.intId, b.intId
FROM MyTable a
CROSS JOIN MyTable b
WHERE a.intId + 1 < b.intId
AND NOT EXISTS (
SELECT NULL FROM MyTable c
WHERE c.intId > a.intId
AND c.intId < b.intId
)
这将给出已删除所有记录的 ID 对。
因此,如果 ID 是 (1, 2, 3, 6, 7, 12),它将返回 (3, 6) 和 (7, 12)。
编辑:
如果表很大,这是非常低效的。以下方法要好得多:
SELECT g.intStartId, MIN(t.intId) AS intEndId
FROM (
SELECT intId AS intStartId
FROM MyTable AS a
WHERE NOT EXISTS (
SELECT NULL FROM MyTable AS b
WHERE b.intId = a.intId + 1
)
) AS g
CROSS JOIN MyTable AS t
WHERE t.intId > g.intStartId
GROUP BY g.intStartId
因此,我们首先找到标记间隙开始的 ID,然后找到大于每个 ID 的最低 ID 来标记间隙的结束。
您可以使用递归查询:
DECLARE @MaxId int
SELECT @MaxId = SELECT IDENT_CURRENT('MyTable');
WITH Ids AS (
SELECT 1 AS intId
UNION ALL
SELECT intId + 1
FROM Ids
WHERE intId < @MaxId
)
SELECT intId
FROM Ids AS i
WHERE NOT EXISTS (
SELECT NULL FROM MyTable AS m
WHERE m.intId = i.intId
)
OPTION (MAXRECURSION 0)
虽然如果表非常大,这不会很有效。
左加入一个数字表并抓取所有为空的数字表,这使用内置的数字表,但最好有自己的
代码的外观示例
create table #bla(id int)
insert #bla values(1)
insert #bla values(2)
insert #bla values(4)
insert #bla values(5)
insert #bla values(9)
insert #bla values(12)
select number from master..spt_values s
left join #bla b on s.number = b.id
where s.type='P'
and s.number < (select MAX(id) from #bla)
and b.id is null
输出
0 3 6 7 8 10 11
请参阅此处:如何从 SQL Server 中的表中返回所有跳过的标识值以获取更多详细信息
一种选择是创建一个临时表/可嵌入的 SQL 语句,其中包含所有可能的 ID(本文概述了一些选项),最多包括表的最大(身份)。
然后,您可以将此规范值列表与您的表左连接,并在右侧过滤空值。
仅关注顺序 ID 列的查询是不够的。如果事务ID
失败,序列可能会在插入期间跳过数字,所以如果你有ID = (1,3)
它并不意味着它ID=2
已被删除,它可能已被跳过。您必须使用某些东西来捕获已删除的记录,例如触发器,或者OUTPUT DELETED.*
- 或者使用某些东西进行比较,例如快照、备份、历史表。
一种完全不同的方法(确实需要重构您的示例)是不要从表中删除,而是有一个单独的已删除项目 id 表(或在表中具有一个显示行状态的字段)。因此,您可以只选择这些数据。(这将围绕@Damir 的观察,即您无法区分删除和插入错误。)