繁体   English   中英

在oracle中,如何以逗号分隔的值对属性进行“分组”?

[英]In oracle, how to 'group by' properties that are in comma separated values?

说,我有一张桌子

Name   Pets
-------------------------
Anna   Cats,Dogs,Hamsters
John   Cats
Jake   Dogs,Cats
Jill   Parrots

我想数一下,有多少人拥有不同类型的宠物。 输出将是这样的

Pets     Owners
---------------
Cats     3
Dogs     2
Hamsters 1
Parrots  1

局限性:

  • 重新设计数据库方案是不切实际的。 如果我能做到,我就不会在这里。
  • 所有逻辑必须在一个SQL查询中完成。
  • 我无法使用结果表并在以后的代码中推断出所有者数。
  • 可以使用内置的Oracle函数,但是不建议编写自定义函数。
  • Oracle版本– 11及更高版本。

正如您所提到的,这是一个糟糕的设计,所以我不羡慕您必须使用它!

尽管我不想说它将对较大的数据集有效,但是您可以按照自己的意愿去做!

假设name列是主键(或至少是唯一键):

with t1 as (select 'Anna' name, 'Cats,Dogs,Hamsters' pets from dual union all
            select 'John' name, 'Cats' pets from dual union all
            select 'Jake' name, 'Dogs,Cats' pets from dual union all
            select 'Jill' name, 'Parrots' pets from dual)
select pet pets,
       count(*) owners 
from   (select name,
               regexp_substr(pets, '(.*?)(,|$)', 1, level, null, 1) pet
        from   t1
        connect by prior name = name
                   and prior sys_guid() is not null
                   and level <= regexp_count(pets, ',') + 1)
group by pet
order by owners desc, pet;


PETS           OWNERS
---------- ----------
Cats                3
Dogs                2
Hamsters            1
Parrots             1

逗号分隔的值存储在单列中是一个糟糕的设计。 您应该考虑将数据标准化 具有这样的设计将总是使您负担处理定界字符串的开销。

无论如何,作为一种解决方法,您可以使用REGEXP_SUBSTRCONNECT BY 将以逗号分隔的字符串拆分为多行,然后计算数量。

还有其他方法可以做到这一点,例如XMLTABLEMODEL子句。 看一下将逗号分隔的字符串分成多行

SQL> WITH sample_data AS(
  2  SELECT 'Anna' NAME, 'Cats,Dogs,Hamsters' pets FROM dual UNION ALL
  3  SELECT 'John' NAME, 'Cats' pets FROM dual UNION ALL
  4  SELECT 'Jake' NAME, 'Dogs,Cats' pets FROM dual UNION ALL
  5  SELECT 'Jill' NAME, 'Parrots' pets FROM dual
  6  )
  7  -- end of sample_data mimicking a real table
  8  SELECT pets,
  9    COUNT(*) cnt
 10  FROM
 11    (SELECT trim(regexp_substr(t.pets, '[^,]+', 1, lines.COLUMN_VALUE)) pets
 12    FROM sample_data t,
 13      TABLE (CAST (MULTISET
 14      (SELECT LEVEL FROM dual CONNECT BY LEVEL <= regexp_count(t.pets, ',')+1
 15      ) AS sys.odciNumberList ) ) lines
 16    ORDER BY NAME,
 17      lines.COLUMN_VALUE
 18    )
 19  GROUP BY pets
 20  ORDER BY cnt DESC;

PETS                      CNT
------------------ ----------
Cats                        3
Dogs                        2
Hamsters                    1
Parrots                     1

SQL>

我的尝试,只用substr和instr :)

with a as (
select 'Anna' as name,   'Cats,Dogs,Hamsters' as pets from dual union all
select 'John',   'Cats' from dual union all
select 'Jake',   'Dogs,Cats' from dual union all
select 'Jill',   'Parrots' from dual
),
b as(
select name, pets, substr(pets, starting_pos, ending_pos - starting_pos) pet
from (
      select name, pets, 
             decode(lvl, 1, 0, instr(a.pets,',',1,lvl-1))+1 starting_pos, 
             instr(a.pets,',',1,lvl) ending_pos
      from (select name, pets||',' pets from a
            )a 
             join (select level lvl from dual connect by level < 10)
          on instr(a.pets,',', 1, lvl) > 0
      )
)
--select * from b
select pet, count(*) from b group by pet;
select x pets ,count(x)   Owners from (     
select  extractvalue(value(x), '/b') x
  from  (select  yourcolumn as str from yourtable) t,
        table(
              xmlsequence(
                          xmltype('<a><b>' || replace(str, ',', '</b><b>') || '</b></a>' ).extract('/*/*')
                         )
             ) x)
             group by x;

/ 用列名(pets)替换yourcolumn,用表名替换yourtable。 /

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM