Возвращает наименьшее целое число, которого нет в списке в SQL

#sql #sqlite

#sql #sqlite

Вопрос:

Предполагается, что у вас есть таблица T(A) , в которой разрешены только положительные целые числа, например:

 1,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18
  

В приведенном выше примере результатом является 10 . Мы всегда можем использовать ORDER BY и DISTINCT для сортировки и удаления дубликатов. Однако, чтобы найти наименьшее целое число, которого нет в списке, я придумал следующий SQL запрос:

    select list.x   1
   from (select x from (select distinct a as x from T order by a)) as list, T
   where list.x   1 not in T limit 1;
  

Моя идея заключается в том, чтобы запустить a counter и 1, проверить, находится ли этот счетчик в list : если это так, верните его, в противном случае увеличьте и посмотрите снова. Однако я должен запустить этот счетчик как 1, а затем увеличить. Этот запрос работает в большинстве случаев, поскольку есть несколько угловых случаев, таких как в 1 . Как я могу добиться этого в SQL или я должен пойти в совершенно другом направлении, чтобы решить эту проблему?

Комментарии:

1. @MarcAudet: Я думаю, требуется вернуть 10.

2. @MarcAudet Я бы ожидал значение 2 в упомянутом угловом регистре. Для первого примера я ожидаю 10 от имени Jonathan.

3. Я отредактировал вопрос, чтобы показать, что 10 это действительно ожидаемый результат для этого примера. @JonathanLeffler

4. @Mihai: теги? PostegresSQL, MySQL и SQL Server?

5. Я бы сгенерировал таблицу, в которой целые числа варьируются от (min, max 1), объединил правой кнопкой мыши в этой таблице и выбрал min (число), где нет совпадения, но генерация диапазона целых чисел отличается от базы данных к базе данных

Ответ №1:

Поскольку SQL работает с наборами, промежуточное значение SELECT DISTINCT a AS x FROM t ORDER BY a является избыточным.

Основной метод поиска разрыва в столбце целых чисел заключается в том, чтобы найти место, где текущая запись плюс 1 не существует. Для этого требуется какое-то самосоединение.

Ваш запрос не за горами, но я думаю, его можно упростить до:

 SELECT MIN(a)   1
  FROM t
 WHERE a   1 NOT IN (SELECT a FROM t)
  

Значение NOT IN действует как своего рода самосоединение. Это ничего не создаст из пустой таблицы, но в противном случае должно быть нормально.

Ответ №2:

SQL Fiddle

 select min(y.a) as a
from
    t x
    right join
    (
        select a   1 as a from t
        union
        select 1
    ) y on y.a = x.a
where x.a is null
  

Это будет работать даже в пустой таблице

Ответ №3:

 SELECT min(t.a) - 1
FROM   t
LEFT   JOIN t t1 ON t1.a = t.a - 1
WHERE  t1.a IS NULL
AND    t.a > 1; -- exclude 0
  

Это находит наименьшее число, большее 1, где следующее меньшее число отсутствует в той же таблице. Возвращается это отсутствующее число.

Это работает даже для отсутствующего 1. Есть несколько ответов, проверяющих в противоположном направлении. Все они завершились бы ошибкой с отсутствующим 1.

SQL Fiddle.

Ответ №4:

Вы можете сделать следующее, хотя вы также можете захотеть определить диапазон — в этом случае вам может понадобиться пара объединений

 SELECT x.id 1 
  FROM my_table x 
  LEFT 
  JOIN my_table y 
    ON x.id 1 = y.id 
 WHERE y.id IS NULL 
 ORDER 
    BY x.id LIMIT 1;
  

Комментарии:

1. как насчет пустой таблицы?

2. Ваш запрос не работает для 2,3,5,7,9,11,13,15,17,19

3. @JonathanLeffler Предлагаемая таблица чисел с левым соединением также подойдет для пустой таблицы.

4. Как я уже сказал, при определенных обстоятельствах вам может потребоваться ОБЪЕДИНЕНИЕ дополнительно

Ответ №5:

Вы всегда можете создать таблицу со всеми числами от 1 до X, а затем объединить эту таблицу с таблицей, которую вы сравниваете. Затем просто найдите в своем операторе SELECT верхнее значение, которого нет в таблице, которую вы сравниваете

 SELECT TOP 1 table_with_all_numbers.number, table_with_missing_numbers.number 
FROM table_with_all_numbers
    LEFT JOIN table_with_missing_numbers 
        ON table_with_missing_numbers.number = table_with_all_numbers.number
WHERE table_with_missing_numbers.number IS NULL
ORDER BY table_with_all_numbers.number ASC;
  

Комментарии:

1. У @ CL OP этот вопрос был помечен как SQL Server, но я предполагаю, что он изменил его

Ответ №6:

В SQLite 3.8.3 или более поздней версии вы можете использовать рекурсивное общее табличное выражение для создания счетчика. Здесь мы прекращаем подсчет, когда находим значение, которого нет в таблице:

 WITH RECURSIVE counter(c) AS (
  SELECT 1
  UNION ALL
  SELECT c   1 FROM counter WHERE c IN t)
SELECT max(c) FROM counter;
  

(Это работает для пустой таблицы или отсутствующего 1 .)

Ответ №7:

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

 select coalesce(min(number),1) from (    
    select min(cnt) number
    from (
        select 
            number, 
            (select count(*) from (select distinct number from numbers) b where b.number <= a.number) as cnt
        from (select distinct number from numbers) a
    ) t1 where number > cnt
    union
    select max(number)   1 number from numbers
) t1
  

http://sqlfiddle.com /#!7/720cc/3

Ответ №8:

Просто другой метод, использующий, ЗА исключением этого времени:

 SELECT a   1 AS missing FROM T
EXCEPT
SELECT a FROM T
ORDER BY missing
LIMIT 1;