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