2013-03-30 30 views
20

Potrzebuję pomocy przy tworzeniu poniższych wyników. Myślałem o sql pivot, ale nie wiem jak z niego korzystać. Spojrzałem na kilka przykładów i nie mogę wymyślić rozwiązania. Wszelkie inne pomysły, jak to osiągnąć, są również mile widziane. Kolumny statusu muszą być generowane dynamicznie.Potrzebuję wiedzieć, jak utworzyć zapytanie crosstab

trzy tabele, aktywa, 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.) 

pożądanych rezultatów:

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

Niektóre dane:

 
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

Co RDBMS używasz? – Taryn

+0

Skąd pochodzą wartości wdrożone (75, 56, 50)? Nie pojawiają się w twoich danych. –

+0

To nie ma dla mnie większego sensu w tej chwili Czy masz kilka przykładów tego, co jest w twoich tabelach, kilka rzeczywistych wierszy ... Musi istnieć pewna wspólność między tabelami jako punktem odniesienia do Połącz je ze sobą ... Jeśli podasz ten szczegół, będę musiał się z tym pogodzić. –

Odpowiedz

40

Ten typ przemiany nazywa się pivot. Nie określiłeś, z której bazy danych korzystasz, więc udzielę odpowiedzi na SQL Server i MySQL.


SQL Server: Jeśli używasz SQL Server 2005+ można realizować funkcję PIVOT.

Jeśli masz znaną liczbę wartości, które chcesz przekonwertować na kolumny, wówczas możesz zakodować zapytanie.

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; 

Zobacz SQL Fiddle with Demo.

Ale jeśli masz nieznaną liczbę wartości status, będziesz musiał użyć dynamicznego sql do wygenerowania listy kolumn w czasie wykonywania.

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) 

Zobacz SQL Fiddle with Demo

To również może być napisane przy użyciu łączną funkcję danej sprawie wyrażenia:

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 

Zobacz SQL Fiddle with Demo


MySQL: Ta baza zrobić nie ma funkcji przestawnej pivot, więc będziesz musiał użyć funkcji agregującej i wyrażenia CASE. To również nie ma funkcji okienkowania, więc trzeba będzie zmienić zapytanie nieco na następujące kwestie:

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; 

Zobacz SQL Fiddle with Demo

Następnie jeśli potrzebujesz dynamiczne rozwiązanie w MySQL, trzeba będzie użyć przygotowane oświadczenie wygenerować ciąg SQL do wykonania:

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; 

Zobacz SQL Fiddle with Demo.

Rezultat jest taki sam dla wszystkich zapytań w obu bazach danych:

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

Serwer SQL o znanej liczbie działał pięknie. Przy nieznanym numerze dynamicznego sql otrzymuję kilka błędów: Msg 1038, poziom 15, Stan 4, wiersz 15 Brak nazwy obiektu lub kolumny lub jest ona pusta. W przypadku instrukcji SELECT INTO sprawdź, czy każda kolumna ma nazwę. W przypadku innych instrukcji szukaj pustych nazw aliasów. Aliasy zdefiniowane jako "" lub [] nie są dozwolone. Zmień alias na poprawną nazwę. – Sam

+1

@Sam Czy możesz edytować [SQL Fiddle] (http://www.sqlfiddle.com/#!3/d7915/7) za pomocą kodu, który próbujesz uruchomić? Umieść kod w prawym panelu, a następnie uruchom sql. Następnie umieść link w komentarzu tutaj. – Taryn

+0

Tworzę tabele, wykonuję kod i wszystko działa dobrze. Kiedy zastosuję to do mojego db, dostaję błąd. Nazwy pól są poprawne. Nie wiem, dlaczego to nie działa. – Sam