我的表有一个唯一的自动增量主键。随着时间的推移,可能会从表中删除条目,因此该字段的值中存在“漏洞”。例如,表格数据可能如下:
ID | Value | More fields...
---------------------------------
2 | Cat | ...
3 | Fish | ...
6 | Dog | ...
7 | Aardvark | ...
9 | Owl | ...
10 | Pig | ...
11 | Badger | ...
15 | Mongoose | ...
19 | Ferret | ...
我对将返回表中缺少ID列表的查询感兴趣。对于上述数据,预期结果为:
ID
----
1
4
5
8
12
13
14
16
17
18
注意:
上述要求的缺点是列表不会返回在ID 19之后创建且已删除的ID。我目前正在代码中解决这个问题,因为我持有创建的最大ID。但是,如果查询可以作为参数MaxID,并且还返回当前max和MaxID之间的ID,那将是一个很好的“奖励”(但肯定不是必须的)。
我目前正在使用MySQL,但考虑转移到SQL Server,所以我希望查询适合两者。另外,如果您使用的是无法在SQLite上运行的任何内容,请提及它,谢谢。
答案 0 :(得分:29)
这个问题经常出现,遗憾的是,最常见(也是最便携)的答案是创建一个临时表来保存应所在的ID,并进行左连接。 MySQL和SQL Server之间的语法非常相似。唯一真正的区别是临时表语法。
在MySQL中:
declare @id int
declare @maxid int
set @id = 1
select @maxid = max(id) from tbl
create temporary table IDSeq
(
id int
)
while @id < @maxid
begin
insert into IDSeq values(@id)
set @id = @id + 1
end
select
s.id
from
idseq s
left join tbl t on
s.id = t.id
where t.id is null
drop table IDSeq
在SQL Server中:
declare @id int
declare @maxid int
set @id = 1
select @maxid = max(id) from tbl
create table #IDSeq
(
id int
)
while @id < @maxid --whatever you max is
begin
insert into #IDSeq values(@id)
set @id = @id + 1
end
select
s.id
from
#idseq s
left join tbl t on
s.id = t.id
where t.id is null
drop table #IDSeq
答案 1 :(得分:19)
以下是SQL Server的查询:
;WITH Missing (missnum, maxid)
AS
(
SELECT 1 AS missnum, (select max(id) from @TT)
UNION ALL
SELECT missnum + 1, maxid FROM Missing
WHERE missnum < maxid
)
SELECT missnum
FROM Missing
LEFT OUTER JOIN @TT tt on tt.id = Missing.missnum
WHERE tt.id is NULL
OPTION (MAXRECURSION 0);
希望这有用。
答案 2 :(得分:17)
我登陆此页面希望找到SQLITE的解决方案,因为这是我在为SQLITE搜索同样的问题时找到的唯一答案。
我找到的最终解决方案来自这篇文章 Float Middle Blog - SQLITE answer
希望它可以帮助其他人: - )
简单的解决方案是:
SELECT DISTINCT id +1
FROM mytable
WHERE id + 1 NOT IN (SELECT DISTINCT id FROM mytable);
天才。
答案 3 :(得分:10)
我知道这是一个老问题,已经有一个接受的答案, 但是使用临时表并不是必需的。修正格式化(对不起双重帖子)。
DECLARE @TEST_ID integer, @LAST_ID integer, @ID integer
SET @TEST_ID = 1 -- start compare with this ID
SET @LAST_ID = 100 -- end compare with this ID
WHILE @TEST_ID <= @LAST_ID
BEGIN
SELECT @ID = (SELECT <column> FROM <table> WHERE <column> = @TEST_ID)
IF @ID IS NULL
BEGIN
PRINT 'Missing ID: ' + CAST(@TEST_ID AS VARCHAR(10))
END
SET @TEST_ID = @TEST_ID + 1
END
答案 4 :(得分:4)
这是Oracle唯一的解决方案。它没有解决完整的问题,但留给可能正在使用Oracle的其他人。
select level id -- generate 1 .. 19
from dual
connect by level <= 19
minus -- remove from that set
select id -- everything that is currently in the
from table -- actual table
答案 5 :(得分:4)
仅限PostgreSQL,受到其他答案的启发。
SELECT all_ids AS missing_ids
FROM generate_series((SELECT MIN(id) FROM your_table), (SELECT MAX(id) FROM your_table)) all_ids
EXCEPT
SELECT id FROM your_table
答案 6 :(得分:2)
我刚刚找到了 Postgres 的解决方案:
select min(gs)
from generate_series(1, 1999) as gs
where gs not in (select id from mytable)
答案 7 :(得分:1)
单个查询可以找到缺少的ID ..
SELECT distinct number
FROM master..spt_values
WHERE number BETWEEN 1 and (SELECT max(id) FROM MyTable)
AND number NOT IN (SELECT id FROM MyTable)
答案 8 :(得分:1)
从表中获取缺失的行
DECLARE @MaxID INT = (SELECT MAX(ID) FROM TABLE1)
SELECT SeqID AS MissingSeqID
FROM (SELECT ROW_NUMBER() OVER (ORDER BY column_id) SeqID from sys.columns) LkUp
LEFT JOIN dbo.TABLE1 t ON t.ID = LkUp.SeqID
WHERE t.ID is null and SeqID < @MaxID
答案 9 :(得分:0)
借用@Eric 提案的修改版本。这是针对 SQL Server 的,并将缺失范围的开始和结束值保存在临时表中。如果差距只是一个值,它会将 NULL
作为最终值以便于可视化。
它会产生这样的输出
|StartId| EndId |
|-------|-------|
| 1 | 10182 |
| 10189 | NULL |
| 10246 | 15000 |
这是脚本,其中 myTable
和 id
需要替换为您的表和标识列。
declare @id bigint
declare @endId bigint
declare @maxid bigint
declare @previousid bigint=0
set @id = 1
select @maxid = max(id) from myTable
create table #IDGaps
(
startId bigint,
endId bigint
)
while @id < @maxid
begin
if NOT EXISTS(select id from myTable where id=@id)
BEGIN
SET @previousid=@id
select top 1 @endId=id from myTable where id>@id
IF @id=@endId-1
insert into #IDGaps values(@id,null)
ELSE
insert into #IDGaps values(@id,@endId-1)
SET @id=@endId
END
ELSE
set @id = @id + 1
end
select * from #IDGaps
drop table #IDGaps
答案 10 :(得分:0)
对我来说最简单的解决方案:创建一个选择,使所有ID达到最大序列值(例如:1000000),然后进行过滤:
with listids as (
Select Rownum idnumber From dual Connect By Rownum <= 1000000)
select * from listids
where idnumber not in (select id from table where id <=1000000)
答案 11 :(得分:0)
SELECT DISTINCT id -1
FROM users
WHERE id != 1 AND id - 1 NOT IN (SELECT DISTINCT id FROM users)
说明:(id - 1).....检查表格中存在的任何先前的身份
(id!= 1).....忽略当前id为1,因为其先前的id将为0。
答案 12 :(得分:0)
使用@PaulSvirin
的答案,我已使用UNION
对其进行了展开,以显示表格中的所有数据,包括NULL
s的缺失记录
WITH Missing(missnum, maxid) AS
(SELECT (SELECT MIN(tmMIN.TETmeetingID)
FROM tblTETMeeting AS tmMIN)
AS missnum,
(SELECT MAX(tmMAX.TETmeetingID)
FROM tblTETMeeting AS tmMAX)
AS maxid
UNION ALL
SELECT missnum + 1, maxid
FROM Missing
WHERE missnum < maxid)
SELECT missnum AS TETmeetingID,
tt.DateID,
tt.WeekNo,
tt.TETID
FROM Missing LEFT JOIN tblTETMeeting tt ON tt.TETmeetingID = Missing.missnum
WHERE tt.TETmeetingID IS NULL
UNION
SELECT tt.TETmeetingID,
tt.DateID,
tt.WeekNo,
tt.TETID
FROM tblTETMeeting AS tt
OPTION ( MAXRECURSION 0 )
工作很棒!
TETmeetingID DateID WeekNo TETID
29 3063 21 1
30 null null null
31 null null null
32 null null null
33 null null null
34 3070 22 1
35 3073 23 1
答案 13 :(得分:0)
将SQL CTE(从Paul Svirin)转换为Oracle版本,它看起来像这样(用表名替换:YOURTABLE):
WITH Missing (missnum,maxid) as (
SELECT 1 missnum, (select max(id) from :YOURTABLE) maxid from dual
UNION ALL
SELECT m.missnum + 1,m.maxid
FROM Missing m
WHERE m.missnum < m.maxid
)
SELECT missnum
FROM Missing
LEFT OUTER JOIN :YOURTABLE tt on tt.id = Missing.missnum
WHERE tt.id is NULL
答案 14 :(得分:0)
几天前,我正在编写一份生产报告,发现有些数据丢失了。丢失的数字非常重要,因此我被要求查找所有缺失数字的列表以供调查。 I posted a blog entry here, with a full demo, including a script to find missing numbers/IDs in a sample table.
建议的脚本很长,所以我不会在这里包含它。以下是使用的基本步骤:
答案 15 :(得分:0)
尝试此查询。这个单一查询足以得到丢失的数字:(请将TABLE_NAME替换为您正在使用的表名称)
select sno as missing from(SELECT @row := @row + 1 as sno FROM
(select 0 union all select 1 union all select 3 union all select 4 union all
select 5 union all select 6 union all select 6 union all select 7 union all
select 8 union all select 9) t,(select 0 union all select 1 union all select 3
union all select 4 union all select 5 union all select 6 union all select 6
union all select 7 union all select 8 union all select 9) t2,(select 0
union all select 1 union all select 3 union all select 4 union all select 5
union all select 6 union all select 6 union all select 7 union all select 8
union all select 9) t3, (select 0 union all select 1 union all select 3 union
all select 4 union all select 5 union all select 6 union all select 6 union all
select 7 union all select 8 union all select 9) t4,
(SELECT @row:=0) as b where @row<1000) as a where a.sno not in
(select distinct b.no from
(select b.*,if(@mn=0,@mn:=b.no,@mn) as min,(@mx:=b.no) as max from
(select ID as no from TABLE_NAME as a) as b,
(select @mn:=0,@mx:=0) as x order by no) as b) and
a.sno between @mn and @mx;
答案 16 :(得分:0)
在 MySQL
中尝试DELIMITER ||
DROP PROCEDURE IF EXISTS proc_missing ||
CREATE PROCEDURE proc_missing()
BEGIN
SET @minID = (SELECT MIN(`id`) FROM `tbl_name` WHERE `user_id`=13);
SET @maxID = (SELECT MAX(`id`) FROM `tbl_name` WHERE `user_id`=13);
REPEAT
SET @tableID = (SELECT `id` FROM `tbl_name` WHERE `id` = @minID);
IF (@tableID IS NULL) THEN
INSERT INTO temp_missing SET `missing_id` = @tableID;
END IF;
SET @minID = @minID + 1;
UNTIL(@minID <= @maxID)
END REPEAT;
END ||
DELIMITER ;
答案 17 :(得分:0)
更新:这个方法花了太长时间,所以我写了一个linux命令来查找文本文件中的空白。它以相反的顺序执行,因此首先将所有id转储到文本文件中,如此;
nohup mysql --password=xx -e 'select id from tablename order by id desc' databasename > /home/ids.txt &
第一行和最后两行只是为了跟踪它花了多长时间。 150万ID(ish)花了我57秒&amp;这是在一个缓慢的服务器上。在i中设置最大ID并拥有它。
T="$(date +%s)"; \
i=1574115; \
while read line; do \
if [[ "$line" != "$i" ]] ; then \
if [[ $i -lt 1 ]] ; then break; fi; \
if [[ $line -gt 1 ]] ; then \
missingsequenceend=$(( $line + 1 )); \
minusstr="-"; \
missingsequence="$missingsequenceend$minusstr$i"; \
expectnext=$(( $line - 1 )); \
i=$expectnext; \
echo -e "$missingsequence"; \
fi; \
else \
i=$(( $i - 1 )); \
fi; \
done \
< /home/ids.txt; \
T="$(($(date +%s)-T))"; \
echo "Time in seconds: ${T}"
示例输出:
1494505-1494507
47566-47572
Time in seconds: 57
另外,我从Eric的答案中得到了语法错误,但在更改分隔符后,在适当的位置使用分号并将其存储在过程中,它可以正常工作。
确保设置正确的最大ID,数据库名称和表名称(它位于选择查询中)。如果您想更改程序名称,请在所有3个位置更改它。
use dbname;
drop procedure if exists dorepeat;
delimiter #
CREATE PROCEDURE dorepeat()
BEGIN
set @id = 1;
set @maxid = 1573736;
drop table if exists IDSeq;
create temporary table IDSeq
(
id int
);
WHILE @id < @maxid DO
insert into IDSeq values(@id);
set @id = @id + 1;
END WHILE;
select
s.id
from
IDSeq s
left join tablename t on
s.id = t.id
where t.id is null;
drop table if exists IDSeq;
END#
delimiter ;
CALL dorepeat;
我也发现了这个查询,但我还没有测试过它。
SELECT a.id+1 AS start, MIN(b.id) - 1 AS end
FROM tablename AS a, tablename AS b
WHERE a.id < b.id
GROUP BY a.id
HAVING start < MIN(b.id)
答案 18 :(得分:-1)
这是我以前找到一个名为tablename
的表的缺失id select a.id+1 missing_ID from tablename a
where a.id+1 not in (select id from tablename b where b.id=a.id+1)
and a.id!=(select id from tablename c order by id desc limit 1)
它将返回缺少的ID。 如果有两(2)个或更多连续缺失id,它将仅返回第一个。
答案 19 :(得分:-1)
只需一个查询即可解决此问题
select lft.id + 1 as missing_ids
from tbl as lft left outer join tbl as rght on lft.id + 1 = rght.id
where rght.id is null and lft.id between 1 and (Select max(id)-1 from tbl)
在Mysql上测试