2012-02-16 15 views
6

Attualmente sto affrontando un problema che la mia conoscenza di Oracle non può risolvere, non sono assolutamente esperto di DB ed è per questo che ti chiedo se hai qualche idea su come risolvere il mio problema di query SQL.Tricky GROUP BY problema su ORACLE

Ecco il mio problema, ho due tabelle, chiamiamoli DEVICE_TABLE e COUNT_TABLE

COUNT_TABLE assomiglia:

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

DEVICE_TABLE assomiglia:

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

I vincoli sono:

DEVICE_TABLE.ID = COUNT_TABLE.DEVICE

Wi-Fi, e-mail, Bluetooth ... sono stringhe che possono essere solo: “Sì”, “No” o “Sconosciuto”

Infine, il mio risultato richiesta SQL atteso è (in base alla mia esempio):

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

In poche parole, lo scopo di questa richiesta è di sommare tutti i dispositivi che sono compatibili con una particolare funzionalità.

Grazie in anticipo se avete qualche idea su come ottenere questo risultato! (Forse non è possibile ...)

+2

'COUNT_TABLE ha il seguente aspetto:' ... perché non hai appena inserito SQL per creare e inserire istruzioni? È molto più facile da leggere. –

+0

Sì, a destra lo pubblicherò se nessuno trova una soluzione per facilitare i test;) – Ajantis

risposta

3

In Oracle 11, è possibile utilizzare la clausola pivot insieme con la clausola 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) 
) 
; 

In alternativa, si potrebbe desiderare di unire le due tabelle esistenti ad un terza tabella che contiene i valori per le tre righe desiderate. E 'probabilmente un po' più facile da leggere, anche:

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; 
+0

Grazie per la tua risposta Ci proverò, ho già 3 risposte ma le tue sembrano per essere ottimizzato (almeno spero) – Ajantis

1

Sono contento di farti piacere - il tuo design db è ben lungi dall'essere perfetto in termini di db relativo. L'unico modo possibile è quello di utilizzare 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

Sono d'accordo sulla progettazione di datamodel sbagliata, ma sfortunatamente non posso cambiarlo:/ Ci stavo pensando, ma temo che non sia così ottimizzato (è necessario analizzare X volte la tabella del mio dispositivo in cui X è il numero di diverse funzionalità) – Ajantis

1

1) Modello di dati potrebbe probabilmente essere migliorato, con la creazione di un tavolo di capacità del dispositivo che avrebbe ID del dispositivo, i riferimenti DEVICE_TABLE.ID e capacità.

Dove si trova Sì nella tabella del dispositivo immettere una riga nella funzionalità del dispositivo e eliminare le colonne funzionalità/funzionalità dalla tabella del dispositivo.

Blocco che:

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 
+0

Sì. So che il mio modello di dati non è corretto. Ma sfortunatamente è quello che devo usare ... Non posso cambiarlo (il cliente è sempre il re ... anche se ha torto!) – Ajantis

0

Se si sta lavorando con Oracle 11g, caratteristica Pivot può essere utilizzato per ottenere il solution.Please rinviare la domanda sotto:

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 

Fare riferimento a SQLFiddle qui: http://sqlfiddle.com/#!4/97793/1/0