2013-03-30 19 views
20

Ho bisogno di aiuto per creare i risultati seguenti. Ho pensato a un perno sql ma non so come usarlo. Ho esaminato alcuni esempi e non è possibile trovare una soluzione. Qualsiasi altra idea su come realizzare questo è anche il benvenuto. Le colonne di stato devono essere generate dinamicamente.Ho bisogno di sapere come creare una query incrociata

hanno tre tavoli, beni, assettypes, assetstatus

 
Table: assets 
assetid  int 
assettag varchar(25) 
assettype int 
assetstatus int 

Table: assettypes 
id   int 
typename varchar(20) (ex: Desktop, Laptop, Server, etc.) 

Table: assetstatus 
id   int 
statusname varchar(20) (ex: Deployed, Inventory, Shipped, etc.) 

risultati desiderati:

 
AssetType  Total Deployed Inventory Shipped  ... 
----------------------------------------------------------- 
Desktop   100  75  20   5  ... 
Laptop   75  56  19   1  ... 
Server   60  50  10   0  ... 

alcuni dati:

 
assets table: 
1,hol1234,1,1 
2,hol1233,1,2 
3,hol3421,2,3 
4,svr1234,3,1 

assettypes table: 
1,Desktop 
2,Laptop 
3,Server 

assetstatus table: 
1,Deployed 
2,Inventory 
3,Shipped 
+0

Cosa RDBMS stai usando? – Taryn

+0

Da dove provengono i valori (75, 56, 50)? Non appaiono nei tuoi dati. –

+0

Al momento non ha molto senso per me Hai alcuni esempi di ciò che è nelle tue tabelle, alcune righe effettive ... Ci deve essere un po 'di comunanza tra le tabelle come punto di riferimento per collegali insieme ... Se fornisci quel dettaglio, ci provo. –

risposta

40

Questo tipo di trasformazione è chiamato un perno. Non hai specificato quale database stai usando, quindi fornirò una risposta per SQL Server e MySQL.


SQL Server: Se si utilizza SQL Server 2005 + è possibile implementare la funzione PIVOT.

Se si dispone di un numero noto di valori che si desidera convertire in colonne, è possibile codificare manualmente la query.

select typename, total, Deployed, Inventory, shipped 
from 
(
    select count(*) over(partition by t.typename) total, 
    s.statusname, 
    t.typename 
    from assets a 
    inner join assettypes t 
    on a.assettype = t.id 
    inner join assetstatus s 
    on a.assetstatus = s.id 
) d 
pivot 
(
    count(statusname) 
    for statusname in (Deployed, Inventory, shipped) 
) piv; 

Vedere SQL Fiddle with Demo.

Ma se si dispone di un numero sconosciuto di valori status, sarà necessario utilizzare sql dinamico per generare l'elenco di colonne in fase di esecuzione.

DECLARE @cols AS NVARCHAR(MAX), 
    @query AS NVARCHAR(MAX) 

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(statusname) 
        from assetstatus 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query = 'SELECT typename, total,' + @cols + ' from 
      (
       select count(*) over(partition by t.typename) total, 
        s.statusname, 
        t.typename 
       from assets a 
       inner join assettypes t 
        on a.assettype = t.id 
       inner join assetstatus s 
        on a.assetstatus = s.id 
      ) x 
      pivot 
      (
       count(statusname) 
       for statusname in (' + @cols + ') 
      ) p ' 

execute(@query) 

Vedi SQL Fiddle with Demo

Questo può anche essere scritta utilizzando una funzione di aggregazione con un caso espressione:

select typename, 
    total, 
    sum(case when statusname ='Deployed' then 1 else 0 end) Deployed, 
    sum(case when statusname ='Inventory' then 1 else 0 end) Inventory, 
    sum(case when statusname ='Shipped' then 1 else 0 end) Shipped 
from 
(
    select count(*) over(partition by t.typename) total, 
    s.statusname, 
    t.typename 
    from assets a 
    inner join assettypes t 
    on a.assettype = t.id 
    inner join assetstatus s 
    on a.assetstatus = s.id 
) d 
group by typename, total 

Vedi SQL Fiddle with Demo


MySQL: Questo database fare es non ha una funzione pivot quindi sarà necessario utilizzare la funzione di aggregazione e un'espressione CASE. Inoltre non hanno funzioni a finestre, in modo da avere a modificare la query leggermente al seguente:

select typename, 
    total, 
    sum(case when statusname ='Deployed' then 1 else 0 end) Deployed, 
    sum(case when statusname ='Inventory' then 1 else 0 end) Inventory, 
    sum(case when statusname ='Shipped' then 1 else 0 end) Shipped 
from 
(
    select t.typename, 
    (select count(*) 
    from assets a1 
    where a1.assettype = t.id 
    group by a1.assettype) total, 
    s.statusname 
    from assets a 
    inner join assettypes t 
    on a.assettype = t.id 
    inner join assetstatus s 
    on a.assetstatus = s.id 
) d 
group by typename, total; 

Vedi SQL Fiddle with Demo

Quindi se avete bisogno di una soluzione dinamica in MySQL, si dovrà utilizzare un dichiarazione preparata per generare la stringa SQL da eseguire:

SET @sql = NULL; 
SELECT 
    GROUP_CONCAT(DISTINCT 
    CONCAT(
     'sum(CASE WHEN statusname = ''', 
     statusname, 
     ''' THEN 1 else 0 END) AS `', 
     statusname, '`' 
    ) 
) INTO @sql 
FROM assetstatus; 

SET @sql 
    = CONCAT('SELECT typename, 
       total, ', @sql, ' 
      from 
      (
       select t.typename, 
       (select count(*) 
       from assets a1 
       where a1.assettype = t.id 
       group by a1.assettype) total, 
       s.statusname 
       from assets a 
       inner join assettypes t 
       on a.assettype = t.id 
       inner join assetstatus s 
       on a.assetstatus = s.id 
      ) d 
      group by typename, total'); 

PREPARE stmt FROM @sql; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 

Vedi SQL Fiddle with Demo.

Il risultato è lo stesso per tutte le query in entrambi i database:

| TYPENAME | TOTAL | DEPLOYED | INVENTORY | SHIPPED | 
----------------------------------------------------- 
| Desktop |  2 |  1 |   1 |  0 | 
| Laptop |  1 |  0 |   0 |  1 | 
| Server |  1 |  1 |   0 |  0 | 
+0

Il server SQL con un numero noto ha funzionato magnificamente. Con un numero sconosciuto sql dinamico ottengo un paio di errori: messaggio 1038, livello 15, stato 4, riga 15 Un nome di oggetto o colonna è mancante o vuoto. Per le istruzioni SELECT INTO, verifica che ogni colonna abbia un nome. Per altre istruzioni, cerca i nomi alias vuoti. Gli alias definiti come "" o [] non sono consentiti. Cambia l'alias con un nome valido. – Sam

+1

@Sam È possibile modificare [SQL Fiddle] (http://www.sqlfiddle.com/#!3/d7915/7) con il codice che si sta tentando di eseguire? Inserire il codice nel pannello di destra e quindi eseguire sql. Quindi pubblica il link in un commento qui. – Taryn

+0

Creo le tabelle, eseguo il codice e tutto funziona correttamente. Quando lo applico al mio db ottengo un errore. I nomi dei campi sono corretti. Non so perché non sta funzionando. – Sam