#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;
Вам вообще не нужно присоединяться; вы можете это сделать:
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 |
Ответ №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;