在SQL中查找序列的间隙而不创建其他表

Mat*_*ari 6 sql postgresql gaps-and-islands

我有一个表invoices有一个字段invoice_number.这是我执行时发生的事情select invoice_number from invoice:

invoice_number
--------------
1
2
3
5
6
10
11
Run Code Online (Sandbox Code Playgroud)

我想要一个SQL,它给我以下结果:

gap_start | gap_end
4         | 4
7         | 9
Run Code Online (Sandbox Code Playgroud)

如何编写SQL来执行此类查询?我正在使用PostgreSQL.

a_h*_*ame 19

使用现代SQL,可以使用窗口函数轻松完成:

select invoice_number + 1 as gap_start, 
       next_nr - 1 as gap_end
from (
  select invoice_number, 
         lead(invoice_number) over (order by invoice_number) as next_nr
  from invoices
) nr
where invoice_number + 1 <> next_nr;
Run Code Online (Sandbox Code Playgroud)

SQLFiddle:http://sqlfiddle.com/#!15/1807/1


tut*_*uju 5

我们可以使用更简单的技术首先通过连接生成的序列列来获取所有缺失值,如下所示:

select series
from generate_series(1, 11, 1) series
left join invoices on series = invoices.invoice_number
where invoice_number is null;
Run Code Online (Sandbox Code Playgroud)

这为我们提供了一系列缺失的数字,在某些情况下它本身就很有用。

为了获得间隙开始/结束范围,我们可以将源表与其自身连接起来。

select invoices.invoice_number + 1 as start, 
       min(fr.invoice_number) - 1 as stop
from invoices
left join invoices r on invoices.invoice_number = r.invoice_number - 1
left join invoices fr on invoices.invoice_number < fr.invoice_number
where r.invoice_number is null
      and fr.invoice_number is not null
group by invoices.invoice_number,
         r.invoice_number;
Run Code Online (Sandbox Code Playgroud)

dbfiddle:https://dbfiddle.uk/? rdbms=postgres_14&fiddle=32c5f3c021b0f1a876305a2bd3afafc9

这可能不如上述解决方案优化,但在可能不支持lead()函数的 SQL 服务器中可能有用。


完全归功于 SILOTA 文档中这个出色的页面: http://www.silota.com/docs/recipes/sql-gap-analysis-missing-values-sequence.html

我强烈建议您阅读它,因为它逐步解释了解决方案。