SQL面试问题
在一次采访中,我得到了以下问题: 给定一个自然数表和一些缺失的自然数,提供两个表的输出,第一个表中的数字差距开始 以秒结束。 例如: 大概是这样的:SQL面试问题,sql,gaps-and-islands,Sql,Gaps And Islands,在一次采访中,我得到了以下问题: 给定一个自然数表和一些缺失的自然数,提供两个表的输出,第一个表中的数字差距开始 以秒结束。 例如: 大概是这样的: SELECT col1, col2 FROM ( SELECT x + 1 as col1, ROW_NUMBER() OVER(ORDER BY x) AS 'rownum' FROM tbl y WHERE NOT EXISTS (SELECT x FROM tbl z WHERE z.x = y
SELECT col1, col2 FROM
(
SELECT x + 1 as col1,
ROW_NUMBER() OVER(ORDER BY x) AS 'rownum'
FROM tbl y
WHERE NOT EXISTS (SELECT x FROM tbl z WHERE z.x = y.x + 1)
AND x <> (SELECT MAX(x) FROM tbl)
) a
INNER JOIN
(
SELECT x - 1 as col2,
ROW_NUMBER() OVER(ORDER BY x) AS 'rownum'
FROM tbl y
WHERE NOT EXISTS (SELECT x FROM tbl z WHERE z.x = y.x - 1)
AND x <> (SELECT MIN(x) FROM tbl)
) b
ON a.rownum = b.rownum
对于不同的DBMS,rownum语法将不同。上述方法可能适用于SQL Server,但我尚未对其进行测试
正如其中一条评论所指出的,许多数据库管理系统都有分析功能,可以简化这一过程。这是SQL Server语法:
CREATE TABLE #temp (columnA int)
INSERT INTO #temp VALUES(1)
INSERT INTO #temp VALUES(2)
INSERT INTO #temp VALUES(4)
INSERT INTO #temp VALUES(5)
INSERT INTO #temp VALUES(8)
INSERT INTO #temp VALUES(9)
INSERT INTO #temp VALUES(13)
SELECT
t1.columnA - 1
FROM
#temp t1
LEFT JOIN #temp t2 ON t1.columnA = t2.ColumnA + 1
WHERE
t2.ColumnA IS NULL
AND t1.ColumnA != (SELECT MIN(ColumnA) from #temp)
SELECT
t1.columnA + 1
FROM
#temp t1
LEFT JOIN #temp t2 ON t1.columnA = t2.ColumnA - 1
WHERE
t2.ColumnA IS NULL
AND t1.ColumnA != (SELECT MAX(ColumnA) from #temp)
DROP table #temp
写了很多关于这些差距和孤岛问题的文章。他的行号解决方案是
WITH C AS
(
SELECT N, ROW_NUMBER() OVER (ORDER BY N) AS RN
FROM t
)
SELECT Cur.N+1,Nxt.N-1
FROM C AS Cur
JOIN C AS Nxt ON Nxt.RN = Cur.RN+1
WHERE Nxt.N-Cur.N>1
以及来自同一来源的没有行号的解决方案
SELECT N+1 AS start_range,
(SELECT MIN(B.N) FROM t AS B WHERE B.N > A.N)-1 AS end_range
FROM t AS A
WHERE NOT EXISTS(SELECT * FROM t AS B WHERE B.N = A.N+1)
AND N< (SELECT MAX(N) FROM t)
这在没有特定于DB的SQL的情况下可以工作,并且可能会变得更干净一些,但它确实可以工作 编辑: 您可以在StackExchange数据资源管理器上看到此操作
SELECT low,high FROM
(
SELECT col1, low
FROM
(Select n1.col1 col1, min(n2.col1) + 1 low
from numbers n1
inner join numbers n2
on n1.col1 < n2.col1
Group by n1.col1) t
WHERE t.low not in (SELECT col1 FROM NUMBERS)
and t.low < (Select MAX(col1) from numbers)
) t
INNER JOIN
(
SELECT col1 - 1 col1, high
FROM
(Select n1.col1 col1 , min(n2.col1) - 1 high
from numbers n1
inner join numbers n2
on n1.col1 < n2.col1
Group by n1.col1) t
WHERE t.high not in (SELECT col1 FROM NUMBERS)
) t2
ON t.col1 = t2.col1
虽然这与Phil Sandler的答案几乎相同,但这应该返回两个单独的表,我认为它看起来更干净,至少在SQL Server中可以工作: DECLARE @temp TABLE (num int) INSERT INTO @temp VALUES (1),(2),(4),(5),(8),(9),(13) DECLARE @min INT, @max INT SELECT @min = MIN(num), @max = MAX(num) FROM @temp SELECT t.num + 1 AS range_start FROM @temp t LEFT JOIN @temp t2 ON t.num + 1 = t2.num WHERE t.num < @max AND t2.num IS NULL SELECT t.num - 1 AS range_end FROM @temp t LEFT JOIN @temp t2 ON t.num - 1 = t2.num WHERE t.num > @min AND t2.num IS NULL 您可以使用Lag函数访问上一行:
create table #a (n int)
insert #a values(1)
insert #a values(2)
insert #a values(4)
insert #a values(5)
insert #a values(8)
insert #a values(9)
insert #a values(13)
select prev + 1, n - 1 from
(select lag(n) over(order by n) as prev, n
from #a) a
where prev < n - 1
一,。步骤1
从id列表中获取当前id和所有可用的下一个id
select l1.id curr_id,l2.id next_id from
id_list l1,id_List l2
where l1.id < l2.id;
二,。步骤2
从上面的列表中,我们将看到所有的组合,但只过滤每个当前ID的一个组合和最小的下一个ID,为此,获取每个当前ID的最小当前ID和最小下一个ID。使用“分组依据每个当前ID”
with id_combinations as
(
select l1.id curr_id,l2.id next_id from
id_list l1,id_List l2
where l1.id < l2.id
)
select min(curr_id)+1 missing_id_start -- Need to add 1 from current available id
,min(next_id)-1 missing_id_end -- Need to subtract 1 from next available id
from id_combinations
group by curr_id
having min(curr_id)+1 < min(next_id) -- Filter to get only the missing ranges
隐马尔可夫模型。。。当我有时间吃午饭的时候,你可以很容易地用像滞后和超前这样的分析函数来做到这一点。。。但这是Oracle或其他支持这些功能的公司所特有的。这是一个可以在任何RDBMS上运行的通用解决方案,还是允许您假设一个特定的实现?我想它必须是通用的,因为其他问题,关于编程,语言不可知。我认为您应该能够使用两个exists语句和一个自比较来填充临时表。一个exists用于查看下一个数字何时不在表中,另一个用于前一个数字何时不在表中。对于first和last,您可以为列的最大值和最小值添加一些内容。@Phil:不再是了:如果您想要两个表的解决方案,只需删除行数子句和外部SELECTUm,这如何产生两个列的结果…?我们提出了相同的概念,但实现略有不同,所以你从我这里得到了一张赞成票。@egrunin:问题是两个表格的输出,而不是两列。@Phil:你是对的:插图显示了一个结果表,文本显示了两个,其他一些答案也一样阅读。不幸的是,除非修改答案,否则我不会收回我的反对票。对不起,关于文字/插图的矛盾我很抱歉,那是我的错。采访中的问题本身是这样的,它是用表格写的,但用逗号分隔的两个值显示了一列。无论如何,谢谢你的回答,这真的很有帮助。我不知道Data Explorer!!欢迎来到Stack Overflow,请解释为什么这会回答这个问题。不鼓励只使用代码的答案。请添加一些解释,说明这是如何解决问题的,或者这与现有答案有何不同。
select l1.id curr_id,l2.id next_id from
id_list l1,id_List l2
where l1.id < l2.id;
with id_combinations as
(
select l1.id curr_id,l2.id next_id from
id_list l1,id_List l2
where l1.id < l2.id
)
select min(curr_id)+1 missing_id_start -- Need to add 1 from current available id
,min(next_id)-1 missing_id_end -- Need to subtract 1 from next available id
from id_combinations
group by curr_id
having min(curr_id)+1 < min(next_id) -- Filter to get only the missing ranges