Oracle 中的递归 SQL 每行只分配一次数据
Recursive SQL in Oracle to allocate data only once per row
我在 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
我想要以下格式的数据。请注意,由于 ITEM_CNT 0 被 ID 1 占用,因此 ID 2 应占用下一个可用号码,即 2.Similarly ITEM_CNT 1 被 ID 3 占用,依此类推。
请注意,如果 item_cnt 已被一个 ID 使用,则不能被另一个 ID 使用。
也总是选择最少的 ITEM_CNT 可用。
ITEM_CNT ID
0 1
2 2
1 3
此外,请注意,我尝试了下面的方法,但是对于每增加一行,我都必须再编写一个递归代码,如果有人可以使下面的代码递归,那就太好了
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
我能够使用一个函数和一个集合来获得答案。
首先,在架构级别定义一个集合。
create type list1 is table of number;
然后创建以下函数
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;
然后你可以像下面这样调用函数来得到想要的结果
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;
我在 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
我想要以下格式的数据。请注意,由于 ITEM_CNT 0 被 ID 1 占用,因此 ID 2 应占用下一个可用号码,即 2.Similarly ITEM_CNT 1 被 ID 3 占用,依此类推。
请注意,如果 item_cnt 已被一个 ID 使用,则不能被另一个 ID 使用。 也总是选择最少的 ITEM_CNT 可用。
ITEM_CNT ID
0 1
2 2
1 3
此外,请注意,我尝试了下面的方法,但是对于每增加一行,我都必须再编写一个递归代码,如果有人可以使下面的代码递归,那就太好了
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
我能够使用一个函数和一个集合来获得答案。
首先,在架构级别定义一个集合。
create type list1 is table of number;
然后创建以下函数
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;
然后你可以像下面这样调用函数来得到想要的结果
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;