如何处理在Oracle中包含逗号分隔或范围字符串值的列

问题描述 投票:0回答:3

使用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的新列将所有数据分成单独的行。

sql oracle plsql oracle12c
3个回答
0
投票

此查询显示如何基于逗号分隔(表示“精确获取这些值”)还是破折号(表示“查找起点和终点之间的所有值”)提取ITEM_LOC个值的列表。我对您的样本数据进行了一些修改(如果其中5个可以完成,就不会显示〜50个值)。

  • #1-6行代表样本数据。
  • 第一个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值。


0
投票

您可以使用以下内容获得所需的数据,其中不存在具有相同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

0
投票

正如其他人所说,以这种方式存储数据是个坏主意。您很可能具有这样的[[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

请注意,此处的输出将与输入不完全相同,因为任何连续的整数都将被压缩为一对。
© www.soinside.com 2019 - 2024. All rights reserved.