【问题标题】:Perform counting for number of occurrence in SQL对 SQL 中出现的次数进行计数
【发布时间】:2020-02-17 06:47:47
【问题描述】:

SQL 进行这样的数据操作,只捕获满足条件的列中的结果,并对出现的次数进行计数,是否常见且方便?如何编写 SQL 代码以生成所需的输出(如果可行)。

名称仅在条件(Cond1 到 Cond5)为“是”时显示。

所需输入

ID Cond1 Cond2 Cond3 Cond4 Cond5 Name1   Name2   Name3   Name4    Name5
1  No    Yes   No    No    Yes   (null)  Result1 n/a     (null)   Result2
2  Yes   No    Yes   No    Yes   Result3 n/a     Result4 (null)   Result5

期望的输出

ID Counting Name
1  1        Result1
1  2        Result2
2  1        Result3
2  2        Result4
2  3        Result5

【问题讨论】:

    标签: sql database oracle window-functions unpivot


    【解决方案1】:

    这可以通过union all 和row_number() 完成:

    select id,  row_number() over(partition by id order by seq) couting, name
    from (
        select id, name1 name, 1 seq from mytable where cond1 = 'Yes'
        union all select id, name2, 2 from mytable where cond2 = 'Yes'
        union all select id, name3, 3 from mytable where cond3 = 'Yes'
        union all select id, name4, 4 from mytable where cond4 = 'Yes'
        union all select id, name5, 5 from mytable where cond5 = 'Yes'
    ) x
    order by id, rn
    

    【讨论】:

    • 干净整洁(与我的怪物查询相反)。
    • @Littlefoot 是的,它是一个简短的查询,但它需要对内部查询中的每个 SELECT 进行一次表/索引扫描,因此它可能效率低下。 db<>fiddle
    【解决方案2】:

    您可以使用CONNECT BY LEVEL 来达到预期的效果,如下所示:

    SELECT
        ID,
        ROW_NUMBER() OVER(PARTITION BY ID ORDER BY LVL) AS "Counting",
        NAME_   AS "Name"
    FROM
        (SELECT
                T.ID,
                DECODE(LVL, 1, COND1, 2, COND2, 3, COND3, 4, COND4, 5, COND5) AS COND,
                DECODE(LVL, 1, NAME1, 2, NAME2, 3, NAME3, 4, NAME4, 5, NAME5) AS NAME_,
                LVL   AS LVL
            FROM
                YOUR_TABLE T join 
           (Select level as lvl from dual CONNECT BY  LEVEL <= 5) on (1=1)
         )
    WHERE COND = 'Yes';
    

    干杯!!

    【讨论】:

    • 嗯,我试过了 - 返回 39 行,这看起来不像 期望的结果。
    【解决方案3】:

    另一种选择:

    SQL> with
      2  test (id, cond1, cond2, cond3, cond4, cond5, name1, name2, name3, name4, name5) as
      3    -- your sample data
      4    (select 1, 'no' , 'yes', 'no' , 'no', 'yes', null     , 'result1', 'n/a'    , null, 'result2' from dual union all
      5     select 2, 'yes', 'no' , 'yes', 'no', 'yes', 'result3', 'n/a'    , 'result4', null, 'result5' from dual
      6    ),
      7  temp as
      8    -- values whose COND column is 'yes'
      9    (select id,
     10       decode(cond1, 'yes', name1) n1,
     11       decode(cond2, 'yes', name2) n2,
     12       decode(cond3, 'yes', name3) n3,
     13       decode(cond4, 'yes', name4) n4,
     14       decode(cond5, 'yes', name5) n5
     15     from test
     16    ),
     17  up as
     18    -- unpivot data
     19    (select *
     20     from temp
     21     unpivot (c_name for pc in (n1, n2, n3, n4, n5))
     22    )
     23    -- final result
     24  select id,
     25         row_number() over (partition by id order by c_name) counting,
     26         c_name as name
     27  from up
     28  order by id;
    
            ID   COUNTING NAME
    ---------- ---------- -------
             1          1 result1
             1          2 result2
             2          1 result3
             2          2 result4
             2          3 result5
    
    SQL>
    

    【讨论】:

      【解决方案4】:

      这是使用 UNPIVOT 的另一个选项。

      create table mytab(id number, 
                         cond1 varchar2(3), 
                         cond2 varchar2(3), 
                         cond3 varchar2(3), 
                         cond4 varchar2(3), 
                         cond5 varchar2(3), 
                         Name1 varchar2(7),
                         Name2 varchar2(7), 
                         Name3 varchar2(7), 
                         Name4 varchar2(7), 
                         Name5 varchar2(7));
      
      insert into mytab values(1,'No','Yes','No','No','Yes',null,'Result1','n/a',null,'Result2');
      
      insert into mytab values(2,'Yes','No','Yes','No','Yes','Result3','n/a','Result4',null,'Result5');
      
      commit;
      
      select * from mytab;
      

      输出:

      ID COND1 COND2 COND3 COND4 COND5 NAME1   NAME2   NAME3   NAME4    NAME5
      1  No    Yes   No    No    Yes   (null)  Result1 n/a     (null)   Result2
      2  Yes   No    Yes   No    Yes   Result3 n/a     Result4 (null)   Result5
      

      基于 UNPIVOT 的解决方案。

      with ns as (
      select id,
             n,
             names 
        from mytab
      unpivot(names for n in (name1 as 'n1', 
                              name2 as 'n2', 
                              name3 as 'n3',
                              name4 as 'n4',
                              name5 as 'n5'))),
      cs as (
      select id,
             n,
             condns 
        from mytab
      unpivot(condns for n in (cond1 as 'n1', 
                               cond2 as 'n2', 
                               cond3 as 'n3',
                               cond4 as 'n4', 
                               cond5 as 'n5')))
      select ns.id, 
             row_number() over(partition by ns.id order by ns.n) counting, 
             ns.names 
        from ns inner join cs
          on ns.id = cs.id 
         and ns.n = cs.n 
         and cs.condns = 'Yes'
       order by 1,2;
      

      输出:

      ID COUNTING NAMES
      1  1        Result1
      1  2        Result2
      2  1        Result3
      2  2        Result4
      2  3        Result5
      

      【讨论】:

        【解决方案5】:

        您可以将UNPIVOT 与列对一起使用,然后对Yes 行进行过滤并使用ROW_NUMBER 分析函数来获取结果的增量索引:

        查询:

        SELECT id,
               ROW_NUMBER() OVER ( PARTITION BY id ORDER BY value ) AS "COUNT",
               name
        FROM   table_name
        UNPIVOT  ( ( cond, name ) FOR value IN (
            ( Cond1, Name1 ) AS 'V1',
            ( Cond2, Name2 ) AS 'V2',
            ( Cond3, Name3 ) AS 'V3',
            ( Cond4, Name4 ) AS 'V4',
            ( Cond5, Name5 ) AS 'V5'
          ) )
        WHERE cond = 'Yes'
        

        测试数据:

        CREATE TABLE table_name (
          ID    NUMBER(10,0) PRIMARY KEY,
          Cond1 VARCHAR2(3) CHECK ( Cond1 IN ( 'Yes', 'No' ) ),
          Cond2 VARCHAR2(3) CHECK ( Cond2 IN ( 'Yes', 'No' ) ),
          Cond3 VARCHAR2(3) CHECK ( Cond3 IN ( 'Yes', 'No' ) ),
          Cond4 VARCHAR2(3) CHECK ( Cond4 IN ( 'Yes', 'No' ) ),
          Cond5 VARCHAR2(3) CHECK ( Cond5 IN ( 'Yes', 'No' ) ),
          Name1 VARCHAR2(10),
          Name2 VARCHAR2(10),
          Name3 VARCHAR2(10),
          Name4 VARCHAR2(10),
          Name5 VARCHAR2(10),
          CHECK ( ( Cond1 = 'Yes' AND Name1 IS NOT NULL ) OR ( Cond1 = 'No' AND ( Name1 IS NULL OR Name1 = 'n/a' ) ) ),
          CHECK ( ( Cond2 = 'Yes' AND Name2 IS NOT NULL ) OR ( Cond2 = 'No' AND ( Name2 IS NULL OR Name2 = 'n/a' ) ) ),
          CHECK ( ( Cond3 = 'Yes' AND Name3 IS NOT NULL ) OR ( Cond3 = 'No' AND ( Name3 IS NULL OR Name3 = 'n/a' ) ) ),
          CHECK ( ( Cond4 = 'Yes' AND Name4 IS NOT NULL ) OR ( Cond4 = 'No' AND ( Name4 IS NULL OR Name4 = 'n/a' ) ) ),
          CHECK ( ( Cond5 = 'Yes' AND Name5 IS NOT NULL ) OR ( Cond5 = 'No' AND ( Name5 IS NULL OR Name5 = 'n/a' ) ) )
        );
        
        INSERT INTO table_name ( ID, Cond1, Cond2, Cond3, Cond4, Cond5, Name1, Name2, Name3, Name4, Name5 )
        SELECT 1, 'No',  'Yes', 'No',  'No', 'Yes', null,      'Result1', 'n/a',     null, 'Result2' FROM DUAL UNION ALL
        SELECT 2, 'Yes', 'No',  'Yes', 'No', 'Yes', 'Result3', 'n/a',     'Result4', null, 'Result5' FROM DUAL;
        

        输出:

        身份证 |计数 |姓名 -: | ----: | :------ 1 | 1 |结果1 1 | 2 |结果2 2 | 1 |结果3 2 | 2 |结果4 2 | 3 |结果5

        db小提琴here

        【讨论】:

        • 问题 1:PARTITION BY id ORDER BY value,是否应该按“名称”排序(而不是按“值”排序?)问题 2:我可以在单个 SQL 查询中拥有多个 UNPIVOT,假设我在同一个表中有 Cond_GROUP2 和 Name_GROUP2 ?谢谢。
        • @TonyChan 这取决于您希望如何订购您的 COUNT 值。 ORDER BY value 将按照它们在列中出现的顺序给出递增计数;而ORDER BY name 将按照name 列中的字符串顺序给出递增计数。对于您的示例数据,输出将与Result1/Result2 相同,无论您按字母顺序比较值还是列中的位置,但如果您要在表中交换它们,那么这两个查询将给出不同的输出。我不能告诉你哪个是有效的,因为它是你的数据和你的输出。
        • @TonyChan 在一条 SQL 语句中可以有多个 UNPIVOT 子句;但是,我不遵循您要实现的目标,并且超出了此问题的范围。如果您想扩展问题,请尝试自己从文档中找到解决方案,如果您无法解决问题,请针对您遇到的问题提出一个新问题。
        • 谢谢。对于问题 1,ORDER BY 值生成正确的结果。 ORDER BY name 实际上把订单搞乱了。对于问题 2,我通过多次使用您的查询并通过主键加入它们来解决。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-17
        • 2023-03-11
        • 2019-05-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多