【问题标题】:Finding Missing Orders in a Column with SQL使用 SQL 在列中查找缺失的订单
【发布时间】:2019-10-29 15:14:04
【问题描述】:

给定下表“订单”:

+--------------------------+
| id|order|country|cstm_id |
+--------------------------+
| 1 |O-1  |DE     |1       |
| 4 |O-2  |AT     |2       |
| 5 |O-3  |DE     |2       |
| 6 |O-5  |AT     |2       |
| 8 |O-6  |DE     |3       |
| 10|O-10 |DE     |3       |
| 11|O-11 |AT     |3       |
| 12|O-123|DE     |1       |
+--------------------------+

id 列和 order 列都缺少值。

我成功地使用此代码查找 id 中的缺失值:

select start, stop from (
  select m.id + 1 as start,
    (select min(id) - 1 from orders as x where x.id > m.id) as stop
  from orders as m
    left outer join orders as r on m.id = r.id - 1
  where r.id is null
) as x
where stop is not null;

想要的结果:

+------------+
| start|stop |
+------------+
| 2    |3    |
| 7    |7    |
| 9    |9    |
+------------+

但是,我在如何对 order 列执行相同操作时遇到了障碍,因为序列是非数字的。

【问题讨论】:

  • 您当前的查询可以使用窗口函数重写以提高效率。

标签: sql postgresql gaps-and-islands


【解决方案1】:

您的第一个查询可以使用窗口函数来简化(而且很可能会更快):

select id + 1 as start, 
       next_id - 1 as stop
from (
  select id, 
         lead(id) over w as next_id,
         lead(id) over w - id as diff
  from orders
  window w as (order by id)
) t
where diff > 1;

清除非数字部分后,同样的方法可以用于“order”值:

with clean_ids as (
  select regexp_replace("order", '[^0-9]', '', 'g')::int as order_id
  from orders
) 
select order_id + 1 as start, 
       next_id - 1 as end
from (
  select order_id, 
         lead(order_id) over w as next_id,
         lead(order_id) over w - order_id as diff
  from clean_ids
  window w as (order by order_id)
) t
where diff > 1;

在线示例:https://rextester.com/NTIEG85119

【讨论】:

    【解决方案2】:

    假设订单号的首字母可以不是“O”,则可以使用正则表达式拆分字母和数字部分并按每个部分排序。

    with orders (id,order_num,country,cstm_id) as
         (values
                 (1 , 'O-1'  , 'DE' ,1)
               , (4 , 'O-2'  , 'AT' ,2)
               , (5 , 'O-3'  , 'DE' ,2)
               , (6 , 'O-5'  , 'AT' ,2)
               , (8 , 'O-6'  , 'DE' ,3)
               , (10, 'O-10' , 'DE' ,3)
               , (11, 'O-11' , 'AT' ,3)
               , (12, 'O-123', 'DE' ,1)
        )
    select *
      from orders 
     order by regexp_replace(order_num, '^([A-Z]+)-(\d+)$','\1') 
            , regexp_replace(order_num, '^([A-Z]+)-(\d+)$','\2')::integer ;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 2022-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多