使用Oracle 12c DB,我有下面的表数据示例,在使用SQL和PL / SQL时需要帮助。
表数据如下:
Table Name: my_data
ID ITEM ITEM_LOC
------- ----------- ----------------
1 Item-1 0,1
2 Item-2 0,1,2,3,4,7
3 Item-3 0-48
4 Item-4 0,1,2,3,4,5,6,7,8
5 Item-5 1-33
6 Item-6 0,1
7 Item-7 0,1,5,8
使用my_data
表中的以上数据,什么是处理此ITEM_LOC的最佳方法,因为我需要将此列中的值用作单个值,即:
0,1表示SQL需要返回0或1或
范围值,即:
0-48表示SQL需要返回0到48之间的值。
这两种情况的返回值应从最低到最高开始,并且一旦处理就不能重复使用。
基于以上所述,根据上面的描述,拥有一个可以获取ID并从尚未使用的ITEM_LOC中返回一个单独值的函数会很棒。这可以是逗号分隔的字符串值或范围字符串值。
ID = 2的期望结果可能是7。对于此ID = 2,无法再次使用ITEM_LOC = 7。
ID = 5的期望结果可能是31。对于此ID = 5,无法再次使用ITEM_LOC = 31。
对于无法再次使用该ID的ITEM_LOC数据,我正在寻找另一个表来保存该数据,或者可能使用名为VALUE_USED的新列将所有数据分成单独的行。
此查询显示如何基于逗号分隔(表示“精确获取这些值”)还是破折号(表示“查找起点和终点之间的所有值”)提取ITEM_LOC
个值的列表。我对您的样本数据进行了一些修改(如果其中5个可以完成,就不会显示〜50个值)。
select
(第7-15行)将逗号分隔的值分成几行select
(第17-26行)使用层次结构查询,该查询将1
添加到起始值,直到项目的结束值。SQL> with my_data (id, item, item_loc) as
2 (select 2, 'Item-2', '0,2,4,7' from dual union all
3 select 7, 'Item-7', '0,1,5' from dual union all
4 select 3, 'Item-3', '0-4' from dual union all
5 select 8, 'Item-8', '5-8' from dual
6 )
7 select id,
8 item,
9 regexp_substr(item_loc, '[^,]+', 1, column_value) loc
10 from my_data
11 cross join table(cast(multiset
12 (select level from dual
13 connect by level <= regexp_count(item_loc, ',') + 1
14 ) as sys.odcinumberlist))
15 where instr(item_loc, '-') = 0
16 union all
17 select id,
18 item,
19 to_char(to_number(regexp_substr(item_loc, '^\d+')) + column_value - 1) loc
20 from my_data
21 cross join table(cast(multiset
22 (select level from dual
23 connect by level <= to_number(regexp_substr(item_loc, '\d+$')) -
24 to_number(regexp_substr(item_loc, '^\d+')) + 1
25 ) as sys.odcinumberlist))
26 where instr(item_loc, '-') > 0
27 order by id, item, loc;
ID ITEM LOC
---------- ------ ----------------------------------------
2 Item-2 0
2 Item-2 2
2 Item-2 4
2 Item-2 7
3 Item-3 0
3 Item-3 1
3 Item-3 2
3 Item-3 3
3 Item-3 4
7 Item-7 0
7 Item-7 1
7 Item-7 5
8 Item-8 5
8 Item-8 6
8 Item-8 7
8 Item-8 8
16 rows selected.
SQL>
我不知道您说的是“不能再次使用item_loc”是什么意思。使用where吗?例如,如果在游标FOR
循环中使用上述查询,则可以-这些值将仅使用一次,因为每次循环迭代都将获取下一个item_loc
值。
您可以使用以下内容获得所需的数据,其中不存在具有相同item_loc数据的m1.id> m2.id
WITH my_subset1
AS (SELECT *
FROM my_data
WHERE Regexp_count(item_loc, '[^,]+') > 1),
my_subset2
AS (SELECT *
FROM my_data
WHERE Regexp_count(item_loc, '[^-]+') > 1),
my_fil_data
AS (SELECT To_number(Regexp_substr(item_loc, '[^,]+', 1, LEVEL)) item_loc,
item,
id
FROM my_subset1
WHERE Regexp_count(item_loc, '[^,]+') > 1
CONNECT BY LEVEL <= Regexp_count(item_loc, '[^,]+')
UNION ALL
SELECT LEVEL - 1,
item,
id
FROM my_subset2
WHERE Regexp_count(item_loc, '[^-]+') > 1
START WITH Regexp_substr(item_loc, '[^-]+', 1, 1) = LEVEL
CONNECT BY LEVEL >= Regexp_substr(item_loc, '[^-]+', 1, 1) + 1
AND LEVEL <= Regexp_substr(item_loc, '[^-]+', 1, 2) + 1)
SELECT Min(item_loc),
id,
item
FROM my_fil_data m1
WHERE NOT EXISTS (SELECT 1
FROM my_fil_data m2
WHERE m2.id < m1.id
AND m2.item_loc = m1.item_loc)
GROUP BY id,
item
ORDER BY id,
item
正如其他人所说,以这种方式存储数据是个坏主意。您很可能具有这样的[[input,并且可能需要这样的display数据,但是您不必按照输入或显示的方式存储数据。
我将根据输入将数据存储为单独的LOC
元素。我假设数据仅包含用逗号分隔的整数,或用连字符分隔的整数对。空格被忽略。逗号分隔的列表不必按任何顺序排列。成对出现时,如果左整数大于右整数,则不返回LOC
元素。create table t as
with input(id, item, item_loc) as (
select 1, 'Item-1', ' 0,1' from dual union all
select 2, 'Item-2', '0,1,2,3,4,7' from dual union all
select 3, 'Item-3', '0-48' from dual union all
select 4, 'Item-4', '0,1,2,3,4,5,6,7,8' from dual union all
select 5, 'Item-5', '1-33' from dual union all
select 6, 'Item-6', '0,1' from dual union all
select 7, 'Item-7', '0,1,5,8,7 - 11' from dual
)
select distinct id, item, loc from input, xmltable(
'let $item := if (contains($X,",")) then ora:tokenize($X,"\,") else $X
for $i in $item
let $j := if (contains($i,"-")) then ora:tokenize($i,"\-") else $i
for $k in xs:int($j[1]) to xs:int($j[count($j)])
return $k'
passing item_loc as X
columns loc number path '.'
);
现在要“使用”一个元素,我只是将其从表中删除:
delete from t where rowid = ( select min(rowid) keep (dense_rank first order by loc) from t where id = 7 );
要以与输入相同的格式返回数据,请使用MATCH_RECOGNIZE
:
select id, item, listagg(item_loc, ',') within group(order by first_loc) item_loc from t match_recognize( partition by id, item order by loc measures a.loc first_loc, a.loc || case count(*) when 1 then null else '-'||b.loc end item_loc pattern (a b*) define b as loc = prev(loc) + 1 ) group by id, item; ID ITEM ITEM_LOC 1 Item-1 0-1 2 Item-2 0-4,7 3 Item-3 0-48 4 Item-4 0-8 5 Item-5 1-33 6 Item-6 0-1 7 Item-7 1,5,7-11
请注意,此处的输出将与输入不完全相同,因为任何连续的整数都将被压缩为一对。