问题
I have the below data in Oracle
ITEM_CNT ID
0 1
1 1
2 1
3 1
0 2
2 2
3 2
0 3
1 3
2 3
3 3
4 3
and I want the data in the below format. Note since ITEM_CNT 0 is taken by ID 1 hence ID 2 should take the next available number which is 2.Similarly ITEM_CNT 1 is taken by ID 3 and so on.
Note if an item_cnt is already taken by an ID it cannot be used by another ID. Also always choose the least ITEM_CNT available.
ITEM_CNT ID
0 1
2 2
1 3
Also, note I tried the below but for each additional row I have to write one more recursive code and if someone can make the below code recursive then it would be great
SELECT Min(m3.item_cnt) item_cnt,
m3.id id,
m3.item item
FROM my_fil_data m3
WHERE m3.item_cnt NOT IN (SELECT Min(m4.item_cnt)
FROM my_fil_data m4
WHERE m3.id > m4.id
AND m4.item_cnt NOT IN (SELECT
Min(m5.item_cnt)
FROM my_fil_data m5
WHERE m4.id > m5.id
AND m5.item_cnt
NOT IN
(SELECT
Min(m6.item_cnt)
FROM my_fil_data m6
WHERE m5.id > m6.id
GROUP BY m6.id)
GROUP BY m5.id)
GROUP BY m4.id)
GROUP BY id,
item
回答1:
I was able to achieve the answer using a function and a collection.
First, define a collection at the schema level.
create type list1 is table of number;
Then create the below function
CREATE OR replace FUNCTION Fn_get_recursive_xyz2(p_id NUMBER)
RETURN NUMBER
AS
l_result NUMBER;
TYPE list2
IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
listrec LIST2;
listrec1 LIST1 := List1();
l_num NUMBER;
l_mm NUMBER;
l_mn NUMBER;
l_cnt NUMBER;
BEGIN
l_num := p_id - 1;
IF p_id > 2 THEN
FOR rec IN 1..l_num LOOP
SELECT Count(1)
INTO l_cnt
FROM my_fil_data
WHERE id = rec;
IF l_cnt = 0 THEN
CONTINUE;
END IF;
SELECT Min(m3.item_cnt) item_cnt
INTO l_mm
FROM my_fil_data m3
WHERE m3.id = rec
AND m3.item_cnt NOT IN (SELECT *
FROM TABLE(listrec1));
listrec1.extend;
Listrec1(listrec1.last) := l_mm;
SELECT Min(m3.item_cnt) item_cnt
INTO l_mn
FROM my_fil_data m3
WHERE m3.id = rec
AND m3.item_cnt NOT IN (SELECT Min(m4.item_cnt) item_cnt
FROM my_fil_data m4
WHERE m3.id > m4.id
--and m4.item_cnt
-- not in (select * from table(listrec)
--)
GROUP BY m4.id)
GROUP BY m3.id;
listrec1.extend;
Listrec1(listrec1.last) := l_mn;
END LOOP;
ELSIF ( p_id = 2 ) THEN
SELECT Count(1)
INTO l_cnt
FROM my_fil_data
WHERE id = 1;
IF l_cnt <> 0 THEN
SELECT Min(m3.item_cnt) item_cnt
INTO l_mn
FROM my_fil_data m3
WHERE m3.id = 1
AND m3.item_cnt NOT IN (SELECT Min(m4.item_cnt) item_cnt
FROM my_fil_data m4
WHERE m3.id > m4.id
--and m4.item_cnt
-- not in (select * from table(listrec)
--)
GROUP BY m4.id)
GROUP BY m3.id;
listrec1.extend;
Listrec1(listrec1.last) := l_mn;
END IF;
END IF;
SELECT Min(m3.item_cnt) item_cnt
INTO l_result
FROM my_fil_data m3
WHERE m3.id = p_id
AND m3.item_cnt NOT IN (SELECT *
FROM TABLE(listrec1));
listrec1.DELETE;
RETURN l_result;
END fn_get_recursive_xyz2;
Then you can call the function to get the desired result like below
WITH fl
AS (SELECT DISTINCT id AS id
FROM my_fil_data)
SELECT Fn_get_recursive_xyz2(id) ITEM_CNT,
id
FROM fl
ORDER BY id;
来源:https://stackoverflow.com/questions/61097906/recursive-sql-in-oracle-to-allocate-data-only-once-per-row