我需要在检查另一个表中的min和max id之后从表中找到丢失的Id

时间:2015-08-09 12:32:00

标签: sql sql-server sql-server-2008

我需要从下面的表#a中找到缺少的ID:

id  |SEQ|Text
1   |1  |AA
1   |3  |CC
1   |4  |DD
1   |5  |EE
1   |6  |FF
1   |7  |GG
1   |8  |HH
1   |10 |JJ
2   |1  |KK
2   |2  |LL
2   |3  |MM
2   |4  |NN
2   |6  |PP
2   |7  |QQ
3   |1  |TT
3   |4  |ZZ
3   |5  |XX

#a的最大值和最小值SEQ存储在另一个表#b中:

id| mn| mx
1 | 1 | 12
2 | 1 | 9
3 | 1 | 5

我的查询是提供正确的输出,但执行费用很高。还有另一种解决方法吗?

with cte
as
(
    select id, mn, mx
    from #b

    union all

    select id, mn, mx -1
    from cte
    where mx-1 > 0
)
select
    cte.id, cte.mx
from
    cte
    left join #a on cte.id = #a.id and cte.mx = #a.seq
where
    #a.seq is null
order by cte.id, cte.mx

此查询主要有两个问题:

  1. 查询运行速度很慢。以上记录只是一个例子。在真正的数据库中,我有50,000行。 enter image description here

  2. 我试图了解检测打嗝的执行计划。但是我无法理解它的一部分,我在Red中强调了这一点。

  3. 如果有人能在这里帮助我会很棒。我被卡住了。

3 个答案:

答案 0 :(得分:2)

您使用递归CTE到generate a set of numbers。这是非常低效的方法(请参阅生成50K数字的图表here)。我建议在数据库中有一个持久的数字表。我个人有一个表Numbers,其中包含100K行和一列Number,这是一个主键,其整数从1到100,000。

一旦有了这样的表格,您的查询就会简化为:

SELECT
    #b.id, #b.mx
FROM
    #b
    INNER JOIN Numbers ON 
        #b.mx >= Numbers.Number AND
        #b.mn <= Numbers.Number -- use this clause if mn can be more than 1
    LEFT JOIN #a ON
        #a.id = #b.id AND
        #a.seq = Numbers.Number
WHERE
    #a.seq IS NULL
ORDER BY #b.id, #b.mx

此外,毋庸置疑,您必须确保#b上的id上有索引,#a上的(id, seq)上的索引。

答案 1 :(得分:1)

我想到的两件事是:

  • 使用数字/计数表。通过创建普通表或使用CTE创建虚拟。用它来查找不存在的数字。

  • 如果没有很多丢失的数字,你可以使用row_number()的技巧找到没有任何空白的数字范围,如下所示:

    select id, min(seq), max(seq) 
    from (
      select
        id,
        seq,
        seq - row_number () over (partition by id order by SEQ asc) GRP
      from
        table1
    ) X group by id, GRP
    order by 1
    

在您找到存在的数字范围后,这当然需要更多处理。

答案 2 :(得分:0)

CTE只是语法,最有可能多次评估 将CTE输出物化为#temp并加入#temp