在oracle中,如何按照'逗号分隔值的属性?

时间:2016-01-13 11:29:19

标签: sql oracle csv count group-by

说,我有一张像

这样的表
Name   Pets
-------------------------
Anna   Cats,Dogs,Hamsters
John   Cats
Jake   Dogs,Cats
Jill   Parrots

我想算一下,有多少人有不同类型的宠物。输出类似于

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

限制:

  • DB方案的重写是不切实际的。如果我能做到,我就不会在这里。
  • 所有逻辑必须在一个SQL查询中完成。
  • 我无法获取结果表,并且稍后会在代码中扣除所有者的数量。
  • 使用内置的Oracle函数是可以的,但不鼓励编写自定义函数。
  • Oracle版本 - 11及以上。

4 个答案:

答案 0 :(得分:2)

这是一个糟糕的设计 - 正如你所提到的 - 所以我不羡慕你不得不使用它!

你可以做你想做的事情,虽然我不想说它会对大型数据集有效!

假设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

答案 1 :(得分:1)

以逗号分隔的值存储在单列中是一种糟糕的设计。您应该考虑规范化数据。有这样的设计总会让你有一个操纵分隔字符串的开销。

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

还有其他方法可以做到这一点,例如 XMLTABLE MODEL 子句。请查看 split the comma-delimited string into multiple rows

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>

答案 2 :(得分:1)

我的尝试,只有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;

答案 3 :(得分:0)

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;

/ 用您的列名(宠物)替换您的列,并使用您的表名替换您的列。 /