SQL - Find missing int values in mostly ordered sequential series

前端 未结 6 582
有刺的猬
有刺的猬 2020-12-06 08:59

I manage a message based system in which a sequence of unique integer ids will be entirely represented at the end of the day, though they will not necessarily arrive in orde

6条回答
  •  有刺的猬
    2020-12-06 09:02

    SET search_path='tmp';
    
    DROP table tmp.table_name CASCADE;
    CREATE table tmp.table_name ( num INTEGER NOT NULL PRIMARY KEY);
    -- make some data
    INSERT INTO tmp.table_name(num) SELECT generate_series(1,20);
    -- create some gaps
    DELETE FROM tmp.table_name WHERE random() < 0.3 ;
    
    SELECT * FROM table_name;
    
    -- EXPLAIN ANALYZE
    WITH zbot AS (
        SELECT 1+tn.num  AS num
        FROM table_name tn
        WHERE NOT EXISTS (
            SELECT * FROM table_name nx
            WHERE nx.num = tn.num+1
            )
        )
    , ztop AS (
        SELECT -1+tn.num  AS num
        FROM table_name tn
        WHERE NOT EXISTS (
            SELECT * FROM table_name nx
            WHERE nx.num = tn.num-1
            )
        )
    SELECT zbot.num AS bot
        ,ztop.num AS top
    FROM zbot, ztop
    WHERE zbot.num <= ztop.num
    AND NOT EXISTS ( SELECT *
        FROM table_name nx
        WHERE nx.num >= zbot.num
        AND nx.num <= ztop.num
        )
    ORDER BY bot,top
        ;
    

    Result:

    CREATE TABLE
    INSERT 0 20
    DELETE 9
     num 
    -----
       1
       2
       6
       7
      10
      11
      13
      14
      15
      18
      19
    (11 rows)
    
     bot | top 
    -----+-----
       3 |   5
       8 |   9
      12 |  12
      16 |  17
    (4 rows)
    

    Note: a recursive CTE is also possible (and probably shorter).

    UPDATE: here comes the recursive CTE ...:

    WITH RECURSIVE tree AS (
        SELECT 1+num AS num
        FROM table_name t0
        UNION
        SELECT 1+num FROM tree tt
        WHERE EXISTS ( SELECT *
            FROM table_name xt
            WHERE xt.num > tt.num
            )
        )
    SELECT * FROM tree
    WHERE NOT EXISTS (
        SELECT *
        FROM table_name nx
        WHERE nx.num = tree.num
        )
    ORDER BY num
        ;
    

    Results: (same data)

     num 
    -----
       3
       4
       5
       8
       9
      12
      16
      17
      20
     (9 rows)
    

提交回复
热议问题