Генерация последовательности идентификаторов с использованием рекурсии в MySQL

#mysql #sql #subquery #common-table-expression #recursive-query

#mysql #sql #подзапрос #common-table-expression #рекурсивный запрос

Вопрос:

У нас есть таблица, определенная transactions как;

 id
-----
 0
 1
 2
 5
 6
 8
 9
 10
 15
 

Как видно, идентификаторы не являются последовательными. Цель состоит в том, чтобы определить отсутствующие идентификаторы. Я пытаюсь сначала сгенерировать все идентификаторы с помощью рекурсии. А затем используйте left join для определения отсутствующих идентификаторов.
Здесь ожидаемый результат должен быть 3,4,7,11,12,13,14.

Вот мой подход;

     WITH RECURSIVE CTE (id) AS(
    
    SELECT MIN(id)
    FROM transactions
    UNION ALL
     SELECT id 1
     FROM CTE
     WHERE id<=(SELECT MAX(id) FROM transactions)
    
    
)

 SELECT *
FROM CTE;
 

Число доходит только до 15 в transactions таблице. Но я получаю следующую ошибку;

ERROR 3636 (HY000) at line 289: Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value. Пожалуйста, обратите внимание, что версия MySQL 8.0.22. Помощь приветствуется.

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

1. Что, если ноль отсутствует?

Ответ №1:

Ваш код не должен вызывать эту ошибку, как показано в this db fiddle .

Однако вы могли бы немного оптимизировать запрос, избегая подзапроса в рекурсивном члене. Это было бы:

 with recursive cte as (
    select min(id) as id, max(id) as max_id from transactions 
    union all
    select id   1, max_id from cte where id < max_id
)
select c.id
from cte c
where not exists (select 1 from transactions t where t.id = c.id)
 

Однако это все еще в значительной степени метод грубой силы. Если у вас много строк и несколько пропущенных чисел, мы можем сделать запрос более эффективным, сгенерировав только диапазоны пропущенных чисел:

 with recursive 
    data as (
        select id, lead(id) over(order by id) lead_id from transactions
    ),
    cte as (
        select id   1 as id, lead_id - 1 as  max_id from data where lead_id > id   1  
        union all
        select id   1, max_id from cte where id < max_id
    )
select id from cte
 

Демонстрация на скрипке DB

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

1. @ GMB Ваше мнение имеет смысл. кажется, что в платформе есть какой-то скрытый случай, когда глубина рекурсии становится превышенной. Мне было интересно, знаете ли вы, как увеличить cte_max_recursion_depth? Спасибо

2. @jay, как вы изменили бы любую другую настройку в mysql: через конфигурационный файл или через оператор set.

3. @ GMB ценю вашу помощь. Существует крайний случай, о котором я задаюсь вопросом. Если id начинается с ненулевого значения, скажем, 3. В этом случае 0,1,2 являются отсутствующими значениями, которые должны быть в выходных данных. Можно ли изменить приведенную выше логику, чтобы включить эти крайние случаи? Спасибо