ORACLE上的Tricky GROUP BY问题

时间:2012-02-16 09:06:57

标签: sql oracle

我目前正面临一个我的Oracle知识无法解决的问题,我绝对不是数据库专家,这就是为什么我问你是否知道如何解决我的SQL查询问题。

这是我的问题,我有两个表,让我们称之为DEVICE_TABLE和COUNT_TABLE

COUNT_TABLE看起来像是:

    DEVICE (Int) PK         |       QUANTITY (Int)
- - - - - - - - - - - - - - - - - - - - - - - - - - -
        1001                |              4
- - - - - - - - - - - - - - - - - - - - - - - - - - -
        1002                |             20
- - - - - - - - - - - - - - - - - - - - - - - - - - - 
        1003                |              1
…

DEVICE_TABLE看起来像:

     ID (Int) PK            |      WiFi (String)            |     Email (String)          |   Bluetooth(String)           |   …
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1001                |             Yes               |               No            |                 No            |   …
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1002                |             Yes               |               Yes           |                 No            |   …
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1003                |             Unknown           |               Unknown       |                 Yes           |   …
…

约束是:

DEVICE_TABLE.ID = COUNT_TABLE.DEVICE

WiFi,电子邮件,蓝牙...字符串只能是:“是”,“否”或“未知”

最后,我预期的SQL请求结果是(基于我的例子):

         Feature        |            Yes           |              No            |            Unknown          
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
        WiFi            |             24           |                 0          |                 1                  
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
       Email            |             20           |                 4          |                 1                  
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    Bluetooth           |              1           |                24          |                 0                   
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
…

简而言之,此请求的目的是将所有与特定功能兼容的设备数量相加。

如果您对如何实现这一点有任何线索,请提前感谢您! (也许这是不可能的......)

4 个答案:

答案 0 :(得分:3)

在Oracle 11中,您可以将pivot子句与unpivot子句一起使用:

with 
count_table as (
     select 1001 device_id,  4 quantity from dual union all
     select 1002 device_id, 20 quantity from dual union all
     select 1003 device_id,  1 quantity from dual 
),
device_table as (
     select 1001 id, 'Yes'     wifi, 'No'       email, 'No'  bluetooth from dual union all
     select 1002 id, 'Yes'     wifi, 'Yes'      email, 'No'  bluetooth from dual union all
     select 1003 id, 'Unknown' wifi, 'Unknown'  email, 'Yes' bluetooth from dual 
)
----------------------------------------
select * from (
      select
        feature,
        yes_no_unknown,
        sum(quantity)  quantity
      from 
         count_table  c join 
         device_table d on c.device_id = d.id
      unpivot  ( yes_no_unknown
                 for feature in (wifi, email, bluetooth)
      ) 
      group by 
      feature,
      yes_no_unknown
)  
pivot ( sum (quantity)
        for yes_no_unknown in ('Yes' as yes, 'No' as no, 'Unknown' as unknown)
)
;

或者,您可能希望将两个现有表连接到第三个表,该表包含三个所需行的值。它也可能更容易阅读:

with 
count_table as (
     select 1001 device_id,  4 quantity from dual union all
     select 1002 device_id, 20 quantity from dual union all
     select 1003 device_id,  1 quantity from dual 
),
device_table as (
     select 1001 id, 'Yes'     wifi, 'No'       email, 'No'  bluetooth from dual union all
     select 1002 id, 'Yes'     wifi, 'Yes'      email, 'No'  bluetooth from dual union all
     select 1003 id, 'Unknown' wifi, 'Unknown'  email, 'Yes' bluetooth from dual 
)
----------------------------------------
select
   f.txt,
   sum(case when ( f.txt = 'wifi'      and d.wifi      = 'Yes' ) or
                 ( f.txt = 'email'     and d.email     = 'Yes' ) or
                 ( f.txt = 'bluetooth' and d.bluetooth = 'Yes' ) 
            then   c.quantity
            else   0 end
      ) yes,
   sum(case when ( f.txt = 'wifi'      and d.wifi      = 'No' ) or
                 ( f.txt = 'email'     and d.email     = 'No' ) or
                 ( f.txt = 'bluetooth' and d.bluetooth = 'No' ) 
            then   c.quantity
            else   0 end
      ) no,
   sum(case when ( f.txt = 'wifi'      and d.wifi      = 'Unknown' ) or
                 ( f.txt = 'email'     and d.email     = 'Unknown' ) or
                 ( f.txt = 'bluetooth' and d.bluetooth = 'Unknown' ) 
            then   c.quantity
            else   0 end
      ) unknown
from 
   count_table  c                                   join 
   device_table d on c.device_id = d.id     cross   join
   (
        select 'wifi'      txt from dual union all
        select 'email'     txt from dual union all
        select 'bluetooth' txt from dual
   ) f
group by 
    f.txt;

答案 1 :(得分:1)

我很高兴为您取悦 - 您的数据库设计在相对数据库方面远非完美。 唯一可行的方法是使用UNION:

select 'WiFi' as Feature, (select count(*) from  DEVICE_TABLE where WiFi = 'Yes') as Yes, (select count(*) from  DEVICE_TABLE where WiFi = 'No') as No 
union
select 'Email' as Feature, (select count(*) from  DEVICE_TABLE where Email = 'Yes') as Yes, (select count(*) from  DEVICE_TABLE where Email = 'No') as No 
...

答案 2 :(得分:1)

1)通过创建具有设备ID,引用DEVICE_TABLE.ID和Capability的设备能力表,可以改进数据模型。

如果设备表中有“是”,请在设备功能中输入一行,并从设备表中删除功能/功能列。

除非:

with Capabilities as (
  select  ID, 'WiFi' as capability, Wifi as has_capability
  from    device_table
  union all 
  select  ID, 'Email', Email 
  from    device_table
  union all 
  select  ID, 'BlueTooth', BlueTooth
  from    device_table
)    
select  C.capability
        , sum(case when C.has_capability = 'Yes' then CNT.quantity end) as Yes
        , sum(case when C.has_capability = 'No' then CNT.quantity end) as No
from    device_table D
        inner join Capabilities C on C.ID = D.ID
        left outer join count_table CNT on CNT.DEVICE = D.ID
group by 
        C.capability
order by 
        C.capability

答案 3 :(得分:0)

如果您使用的是Oracle 11g,可以使用Pivot功能获取解决方案。请参阅以下查询:

select features,nvl(yes,0) yes,nvl(no,0) no,nvl(unknown,0) unknown from (
select * from (select 'Wifi' as features,wifi,nvl(quantity,0) quantity from count_table, device_table where id = device_id)
pivot (sum(nvl(quantity,0)) for Wifi in ('Yes' as yes,'No' as no,'Unknown' as unknown))
Union all
select * from (select 'Bluetooth' as features,bluetooth,nvl(quantity,0) quantity from count_table, device_table where id = device_id)
pivot (sum(nvl(quantity,0)) for bluetooth in ('Yes' as yes,'No' as no,'Unknown' as unknown))
union all
select * from (select 'Email' as features,Email,nvl(quantity,0) quantity from count_table, device_table where id = device_id)
pivot (sum(nvl(quantity,0)) for Email in ('Yes' as yes,'No' as no,'Unknown' as unknown))
)
order by yes desc

请参阅SQLFiddle:http://sqlfiddle.com/#!4/97793/1/0