【问题标题】:Tricky GROUP BY issue on ORACLEORACLE 上棘手的 GROUP BY 问题
【发布时间】:2012-03-07 16:11:57
【问题描述】:

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

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

COUNT_TABLE 看起来像:

设备(内部)PK |数量(整数) - - - - - - - - - - - - - - - - - - - - - - - - - 1001 | 4 - - - - - - - - - - - - - - - - - - - - - - - - - 1002 | 20 - - - - - - - - - - - - - - - - - - - - - - - - - 1003 | 1 …

DEVICE_TABLE 看起来像:

ID(内部)PK | WiFi(字符串)|电子邮件(字符串)|蓝牙(字符串) | … - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1001 |是 |没有 |没有 | … - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1002 |是 |是 |没有 | … - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1003 |未知 |未知 |是 | … …

约束是:

DEVICE_TABLE.ID = COUNT_TABLE.DEVICE

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

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

特色 |是 |没有 |未知 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 无线网络 | 24 | 0 | 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 电子邮件 | 20 | 4 | 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 蓝牙 | 1 | 24 | 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - …

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

如果您对如何实现此目标有任何线索,请提前致谢! (也许不可能……)

【问题讨论】:

  • COUNT_TABLE looks like : ...您为什么不发布 SQL 创建和插入语句?它更容易阅读。
  • 是的,如果没有人找到简化测试的解决方案,我会发布它;)

标签: sql oracle


【解决方案1】:

在 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;

【讨论】:

  • 谢谢你的回答我会试试的,我已经得到了 3 个答案,但你的似乎是最优化的(至少我希望如此)
【解决方案2】:

我很高兴取悦您 - 就相对 db 而言,您的 db 设计远非完美。 唯一可能的方法是使用 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 
...

【讨论】:

  • 我同意错误的数据模型设计,但不幸的是我无法改变它:/我正在考虑这个,但我担心它没有那么优化(需要解析 X 次我的设备表 X 是不同特征的数量)
【解决方案3】:

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

【讨论】:

  • 是的,我知道我的数据模型不正确。但不幸的是,这是我需要使用的……我无法改变它(客户永远是王者……即使他错了!)
【解决方案4】:

如果您使用的是 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

【讨论】:

    猜你喜欢
    • 2012-03-08
    • 2011-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 1970-01-01
    • 2012-10-02
    相关资源
    最近更新 更多