Обнаружен цикл при выполнении рекурсивного запроса

#sql #oracle #recursion #common-table-expression

Вопрос:

Я использую рекурсивный запрос CTE для получения вывода ниже, но не знаю, почему он выдает «обнаружен цикл при выполнении рекурсивного запроса С запросом». Кто-нибудь может, пожалуйста, сказать мне, что не так с моим запросом?

мой вопрос:

 WITH
cte (order_id,
     product_id,
     quantity,
     cnt)
AS
    (SELECT order_id,
            product_id,
            1 as quantity,
            1 as cnt
       FROM order_tbl2        
     UNION ALL
     SELECT a.order_id,
            a.product_id,
            b.quantity,
            b.cnt   1
       FROM order_tbl2 A INNER JOIN cte b ON a.product_id = b.product_id
      WHERE  b.cnt   1 < a.quantity)
 

ВЫБЕРИТЕ идентификатор заказа, идентификатор продукта, количество
ОТ cte;

введите описание изображения здесь

сценарий таблицы/данных:

 CREATE TABLE ORDER_TBL2
(
ORDER_PAY   DATE,
ORDER_ID    VARCHAR2(10 BYTE),
PRODUCT_ID  VARCHAR2(10 BYTE),
QUANTITY    NUMBER(5),
PRICE       NUMBER(5)
);

Insert into ORDER_TBL2
 (ORDER_PAY, ORDER_ID, PRODUCT_ID, QUANTITY, PRICE)
Values
 (TO_DATE('5/1/2015', 'MM/DD/YYYY'), 'ORD1', 'PROD1', 5, 5);
Insert into ORDER_TBL2
 (ORDER_PAY, ORDER_ID, PRODUCT_ID, QUANTITY, PRICE)
Values
 (TO_DATE('5/1/2015', 'MM/DD/YYYY'), 'ORD2', 'PROD2', 2, 10);
Insert into ORDER_TBL2
 (ORDER_PAY, ORDER_ID, PRODUCT_ID, QUANTITY, PRICE)
Values
 (TO_DATE('5/1/2015', 'MM/DD/YYYY'), 'ORD3', 'PROD3', 3, 25);

COMMIT;
 

Ответ №1:

В рекурсивном члене вы в настоящее время только присоединяетесь a.product_id = b.product_id , а не a.order_id = b.order_id AND a.product_id = b.product_id ; что здесь не имеет прямого значения, но имело бы значение, если бы разные заказы включали одни и те же продукты, что, вероятно, в реальном мире.

Однако ваши данные и запрос на самом деле, похоже, не имеют цикла. Похоже, вы натыкаетесь на то, что кажется ошибкой в соединениях ANSI; добавление cycle предложения не показывает никаких циклических строк, как ожидалось — и заставляет его работать!; и это работает с соединениями старого стиля:

 WITH
    cte (order_id,
         product_id,
         quantity,
         cnt)
    AS
        (SELECT order_id,
                product_id,
                1 as quantity,
                1 as cnt
           FROM order_tbl2        
         UNION ALL
         SELECT a.order_id,
                a.product_id,
                b.quantity,
                b.cnt   1
           FROM order_tbl2 A, cte b
          WHERE b.cnt   1 < a.quantity
            AND a.order_id = b.order_id
            AND a.product_id = b.product_id
            )
SELECT order_id, product_id, quantity
  FROM cte;
 

db<>скрипка

Вам вообще не нужно присоединяться; вы можете это сделать:

 WITH
    cte (order_id,
         product_id,
         quantity,
         cnt)
    AS
        (SELECT order_id,
                product_id,
                quantity,
                1 as cnt
           FROM order_tbl2        
         UNION ALL
         SELECT b.order_id,
                b.product_id,
                b.quantity,
                b.cnt   1
           FROM cte b
          WHERE  b.cnt < b.quantity)
SELECT order_id, product_id, 1 as quantity
  FROM cte;
 

который назначает фиксированное количество 1 в окончательном выборе, или:

 WITH
    cte (order_id,
         product_id,
         real_quantity,
         quantity,
         cnt)
    AS
        (SELECT order_id,
                product_id,
                quantity as real_quantity,
                1 as quantity,
                1 as cnt
           FROM order_tbl2        
         UNION ALL
         SELECT b.order_id,
                b.product_id,
                b.real_quantity,
                b.quantity,
                b.cnt   1
           FROM cte b
          WHERE  b.cnt < b.real_quantity)
SELECT order_id, product_id, quantity
  FROM cte;
 

который присваивает его внутри и должен отслеживать исходное количество в качестве нового псевдонима.

Для обоих из них я удалил 1 сравнение количества, так как это заставляло его останавливаться слишком рано; с order by добавлением они оба получают:

ИДЕНТИФИКАТОР ЗАКАЗА PRODUCT_ID Количество
ОРД1 ПРОД1 1
ОРД1 ПРОД1 1
ОРД1 ПРОД1 1
ОРД1 ПРОД1 1
ОРД1 ПРОД1 1
ОРД2 ПРОД2 1
ОРД2 ПРОД2 1
ОРД3 ПРОД3 1
ОРД3 ПРОД3 1
ОРД3 ПРОД3 1

db<>скрипка

Ответ №2:

Я думаю, что вы пытаетесь расширить строки. Если да, то в этом нет join необходимости:

 WITH cte (order_id, product_id, quantity, cnt) AS (
      SELECT order_id, product_id, quantity, 1 as cnt
      FROM order_tbl2        
      UNION ALL
      SELECT order_id, product_id, quantity, cnt   1
      FROM cte
      WHERE cnt < a.quantity
     )
SELECT *
FROM cte;