2013-03-30 4 views
20

Ich brauche Hilfe beim Erstellen der folgenden Ergebnisse. Ich dachte an einen Sql Pivot, aber ich weiß nicht, wie ich ihn benutzen soll. Guckte auf ein paar Beispiele und kann keine Lösung finden. Andere Ideen, wie dies zu erreichen ist, sind ebenfalls willkommen. Statusspalten müssen dynamisch generiert werden.Ich muss wissen, wie man eine Kreuztabellenabfrage erstellt

haben drei Tabellen, Vermögen, 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.) 

Wunschergebnisse:

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

Einige Daten:

 
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

Welche RDBMS verwenden Sie? – Taryn

+0

Woher kommen die (75, 56, 50) implementierten Werte? Sie erscheinen nicht in Ihren Daten. –

+0

Das macht für mich im Moment keinen großen Sinn Haben Sie einige Beispiele dafür, was in Ihren Tabellen steht, ein paar tatsächliche Zeilen ... Es muss einige Gemeinsamkeiten zwischen den Tabellen als Bezugspunkt geben verbinden Sie sie miteinander ... Wenn Sie dieses Detail liefern, werde ich es ausprobieren. –

Antwort

40

Diese Art der Transformation ein Dreh genannt wird. Sie haben nicht angegeben, welche Datenbank Sie verwenden, daher werde ich eine Antwort für SQL Server und MySQL geben.


SQL Server: Wenn Sie SQL Server verwenden 2005+ Sie die PIVOT Funktion implementieren können.

Wenn Sie eine bekannte Anzahl von Werten haben, die Sie in Spalten konvertieren möchten, können Sie die Abfrage fest codieren.

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; 

Siehe SQL Fiddle with Demo.

Aber wenn Sie eine unbekannte Anzahl von status Werte haben, dann müssen Sie dynamische SQL verwenden, um die Liste der Spalten zur Laufzeit zu generieren.

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) 

Siehe SQL Fiddle with Demo

Dies kann auch mit einem Fall unter Verwendung des Ausdrucks eine Aggregatfunktion geschrieben werden:

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 

Siehe SQL Fiddle with Demo


MySQL: Diese Datenbank tun Es hat keine Pivot Funktion, so dass Sie die Aggregatfunktion und eine CASE Ausdruck verwenden müssen. Es hat auch nicht funktioniert Windowing, so dass Sie die Abfrage leicht an folgende ändern müssen:

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; 

Siehe SQL Fiddle with Demo

Dann, wenn Sie eine dynamische Lösung in MySQL benötigen, müssen Sie ein verwenden vorbereitete Anweisung generieren die sQL-Zeichenfolge ausführen:

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; 

Siehe SQL Fiddle with Demo.

Das Ergebnis ist das gleiche für alle Anfragen in beiden Datenbanken:

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

SQL-Server mit einer bekannten Nummer funktionierte wunderbar. Mit einer unbekannten Nummer dynamic sql bekomme ich ein paar Fehler: Msg 1038, Ebene 15, Status 4, Zeile 15 Ein Objekt oder Spalte Name fehlt oder leer. Überprüfen Sie für SELECT INTO-Anweisungen, dass jede Spalte einen Namen hat. Suchen Sie bei anderen Anweisungen nach leeren Alias-Namen. Aliase, die als "" oder [] definiert sind, sind nicht erlaubt. Ändern Sie den Alias ​​in einen gültigen Namen. – Sam

+1

@Sam Können Sie die [SQL Fiddle] (http://www.sqlfiddle.com/#!3/d7915/7) mit dem Code bearbeiten, den Sie ausführen möchten? Platziere den Code im rechten Bereich und führe sql aus. Dann poste den Link in einem Kommentar hier. – Taryn

+0

Ich erstelle die Tabellen, führe den Code aus und alles funktioniert gut. Wenn ich es auf meine Datenbank anwende, erhalte ich einen Fehler. Feldnamen sind korrekt. Ich weiß nicht, warum es nicht funktioniert. – Sam

Verwandte Themen