【问题标题】:In oracle, how to 'group by' properties that are in comma separated values?在 oracle 中,如何对逗号分隔值中的属性进行“分组”?
【发布时间】:2016-04-18 08:35:09
【问题描述】:

说,我有一张类似的桌子

命名宠物 ------------------------- 安娜猫、狗、仓鼠 约翰猫 杰克狗,猫 吉尔鹦鹉

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

宠物主人 --------------- 猫 3 狗 2 仓鼠 1 鹦鹉 1

限制:

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

【问题讨论】:

  • 这个表不满足数据库表的最低要求!至少应该满足第一个规范化形式。
  • 可能的宠物列表是否有限?存储在单独的表中?
  • yasserkabbout:最初“Pets”字段的意思是作为日志字段,应该只显示在报告中。 StanislavL:有限?不是数学意义上的。在单独的表中?是的,但是用户可以从该表中删除记录,但此删除不会(也不能)影响此处显示的表。

标签: sql oracle csv count group-by


【解决方案1】:

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

你可以做你想做的事,虽然我不想说它对更大的数据集也有性能!

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

【讨论】:

    【解决方案2】:

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

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

    还有其他方法可以做到这一点,例如 XMLTABLEMODEL 子句。看看 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>
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我更喜欢这个答案,因为它不使用正则表达式
      【解决方案4】:
      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;
      

      /将 yourcolumn 替换为您的列名(宠物),将 yourtable 替换为您的表名。/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-01
        • 1970-01-01
        • 2017-07-04
        • 2018-05-19
        • 2012-04-14
        相关资源
        最近更新 更多