2

我管理一个基于消息的系统,其中一系列唯一整数 ID 将在一天结束时完全表示,尽管它们不一定按顺序到达。

我正在寻找使用 SQL 查找本系列中缺少的 id 的帮助。如果我的列值如下所示,在这种情况下,如何找到我在这个序列中缺少的 id 6

该序列将在每天的任意点开始和结束,因此每次运行时 min 和 max 会有所不同。来自 Perl 背景,我通过那里的一些正则表达式。

ids
1
2
3
5
4
7
9
8
10

帮助将不胜感激。

编辑:我们运行 oracle

编辑2:谢谢大家。下周我将在办公室介绍您的解决方案。

Edit3:我暂时解决了以下问题,ORIG_ID 是原始 id 列,MY_TABLE 是源表。在仔细查看我的数据时,除了字符串中的数字数据之外,还有多种情况。在某些情况下,存在非数字字符的前缀或后缀。在其他情况下,数字 id 中混有破折号或空格。除此之外,id 会定期出现多次,所以我包括了 distinct。

我将不胜感激任何进一步的输入,特别是关于去除非数字字符的最佳途径。

SELECT 
   CASE
      WHEN NUMERIC_ID + 1 = NEXT_ID - 1
         THEN TO_CHAR( NUMERIC_ID + 1 )
      ELSE TO_CHAR( NUMERIC_ID + 1 ) || '-' || TO_CHAR( NEXT_ID - 1 )
   END
   MISSING_SEQUENCES
   FROM
   (
      SELECT
         NUMERIC_ID,
         LEAD (NUMERIC_ID, 1, NULL)
            OVER 
            (
               ORDER BY
                 NUMERIC_ID
                 ASC
            )
            AS NEXT_ID
         FROM 
         (
             SELECT
                DISTINCT TO_NUMBER( REGEXP_REPLACE(ORIG_ID,'[^[:digit:]]','') ) 
                AS NUMERIC_ID
             FROM MY_TABLE
         )
    ) WHERE NEXT_ID != NUMERIC_ID + 1
4

6 回答 6

6

我去过那儿。

FOR ORACLE:

前段时间在网上发现了这个非常有用的查询并记下了,但是我现在不记得该站点了,您可以"GAP ANALYSIS"在Google上搜索。

SELECT   CASE
             WHEN ids + 1 = lead_no - 1 THEN TO_CHAR (ids +1)
          ELSE TO_CHAR (ids + 1) || '-' || TO_CHAR (lead_no - 1)
         END
             Missing_track_no
   FROM   (SELECT   ids,
                    LEAD (ids, 1, NULL)
                     OVER (ORDER BY ids ASC)
                        lead_no
             FROM   YOURTABLE
             )
   WHERE   lead_no != ids + 1

在这里,结果是:

MISSING _TRACK_NO
-----------------
       6

如果有多个间隙,比如 2、6、7、9,那么它将是:

MISSING _TRACK_NO
-----------------
        2
       6-7
        9
于 2011-12-03T15:40:11.930 回答
4

这有时称为排除连接。也就是说,尝试进行连接并仅返回不匹配的行。

SELECT t1.value-1
FROM ThisTable AS t1
LEFT OUTER JOIN ThisTable AS t2
  ON t1.id = t2.value+1
WHERE t2.value IS NULL

请注意,这将始终报告至少一行,即 MIN value

此外,如果有两个或多个数字的差距,它只会报告一个缺失值。

于 2011-12-03T00:14:07.617 回答
2

您没有说明您的 DBMS,所以我假设 PostgreSQL:

select aid as missing_id
from generate_series( (select min(id) from message), (select max(id) from message)) as aid
  left join message m on m.id = aid
where m.id is null;  

这将报告表中最小和最大 id 之间的序列中的任何缺失值(包括大于 1 的间隙)

psql (9.1.1)
键入“帮助”以获得帮助。

postgres=> 从消息中选择 *;
 ID
----
  1
  2
  3
  4
  5
  7
  8
  9
 11
 14
(10 行)


postgres=> 选择辅助作为missing_id
postgres-> from generate_series( (select min(id) from message), (select max(id) from message)) 作为辅助
postgres-> 在 m.id = 辅助上左加入消息 m
postgres-> 其中 m.id 为空;
 缺少_id
------------
          6
         10
         12
         13
(4 行)
postgres=>
于 2011-12-03T00:51:37.620 回答
0

我在mysql中应用它,它工作..

mysql> select * from sequence;
+--------+
| number |
+--------+
|      1 |
|      2 |
|      4 |
|      6 |
|      7 |
|      8 |
+--------+
6 rows in set (0.00 sec)

mysql> SELECT t1.number - 1 FROM sequence AS t1 LEFT OUTER JOIN sequence AS t2 O
N t1.number = t2.number +1 WHERE t2.number IS NULL;
+---------------+
| t1.number - 1 |
+---------------+
|             0 |
|             3 |
|             5 |
+---------------+
3 rows in set (0.00 sec)
于 2011-12-03T00:24:06.367 回答
0
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
    ;

结果:

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)

注意:递归 CTE 也是可能的(并且可能更短)。

更新:递归 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
    ;

结果:(相同数据)

 num 
-----
   3
   4
   5
   8
   9
  12
  16
  17
  20
 (9 rows)
于 2011-12-03T15:40:19.953 回答
0
select student_key, next_student_key
      from (
    select student_key, lead(student_key) over (order by student_key) next_fed_cls_prgrm_key
      from student_table
           )
where student_key <> next_student_key-1;
于 2013-07-31T15:38:02.933 回答