Хитрый выпуск GROUP BY на ORACLE - PullRequest
6 голосов
/ 16 февраля 2012

В настоящее время я сталкиваюсь с проблемой, которую мои знания 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, электронная почта, Bluetooth ... - это строки, которые могут быть только: «Да», «Нет» или «Неизвестно»

Наконец, мой ожидаемый результат запроса SQL (на основе моего примера):

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

Кратко говоря, целью этого запроса является суммирование всех устройств, совместимых с определенной функцией.

Заранее спасибо, если у вас есть какие-либо подсказки, как этого добиться! (Может быть, это невозможно ...)

Ответы [ 4 ]

3 голосов
/ 16 февраля 2012

В 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 голос
/ 16 февраля 2012

1) Модель данных, вероятно, можно улучшить, создав таблицу возможностей устройства, которая будет иметь идентификатор устройства, ссылки 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
1 голос
/ 16 февраля 2012

Я рад вас радовать - ваш дизайн БД далек от совершенства с точки зрения относительного БД.Единственный возможный способ - использовать 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 
...
0 голосов
/ 14 января 2016

Если вы работаете с 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...