SQL - Найти пропущенные значения int в большинстве упорядоченных последовательных - PullRequest
2 голосов
/ 03 декабря 2011

Я управляю системой, основанной на сообщениях, в которой последовательность уникальных целочисленных идентификаторов будет полностью представлена ​​в конце дня, хотя они не обязательно будут поступать по порядку.

Я ищу помощь в поиске пропущенных идентификаторов в этой серии с использованием SQL. Если значения моего столбца похожи на приведенные ниже, как я могу найти, какие идентификаторы отсутствуют в этой последовательности, в данном случае 6?

Последовательность будет начинаться и заканчиваться в произвольной точке каждый день, поэтому min и max будут различаться при каждом запуске. Исходя из фона Perl я через некоторое регулярное выражение там.

ids
1
2
3
5
4
7
9
8
10

Помощь будет высоко ценится.

Редактировать: Мы запускаем оракула

Edit2: Спасибо всем. Я рассмотрю ваши решения на следующей неделе в офисе.

Edit3: я остановился на чем-то похожем на приведенное ниже: ORIG_ID - исходный столбец id, а MY_TABLE - исходная таблица. Если присмотреться к моим данным, есть множество случаев, помимо числовых данных в строке. В некоторых случаях есть префикс или суффикс нечисловых символов. В других есть тире или пробелы, смешанные в числовой идентификатор. Помимо этого, идентификаторы периодически появляются несколько раз, поэтому я включил различные.

Буду признателен за любые дальнейшие замечания, особенно в отношении наилучшего способа удаления нецифровых символов.

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

Ответы [ 6 ]

5 голосов
/ 03 декабря 2011

Я был там.

FOR ORACLE:

Я нашел этот чрезвычайно полезный запрос в сети некоторое время назад и записал его, но сейчас я не помню сайт, вы можете искать в Google "GAP ANALYSIS" .

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
4 голосов
/ 03 декабря 2011

Это иногда называется исключающим соединением. То есть попробуйте выполнить объединение и вернуть только те строки, в которых нет совпадений.

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.

Кроме того, если есть пропуски из двух или более чисел, он сообщит только об одном пропущенном значении.

1 голос
/ 03 декабря 2011

Вы не указали свою СУБД, поэтому я предполагаю, что 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;  

Это сообщит о любом пропущенном значении в последовательности между минимальным и максимальным идентификатором в вашей таблице (включая пробелы, превышающие единицу)

psql (9.1.1)
Type "help" for help.

postgres=> select * from message;
 id
----
  1
  2
  3
  4
  5
  7
  8
  9
 11
 14
(10 rows)


postgres=> select aid as missing_id
postgres-> from generate_series( (select min(id) from message), (select max(id) from message)) as aid
postgres->   left join message m on m.id = aid
postgres-> where m.id is null;
 missing_id
------------
          6
         10
         12
         13
(4 rows)
postgres=>
0 голосов
/ 31 июля 2013
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;
0 голосов
/ 03 декабря 2011
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)
0 голосов
/ 03 декабря 2011

Я применил его в 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)
...