2013-03-30 80 views
20

我需要幫助創建下面的結果。我想到了一個SQL數據透視表,但我不知道如何使用它。看了幾個例子,不能提出解決方案。任何其他想法如何實現這一點也是受歡迎的。狀態列必須動態生成。我需要知道如何創建一個交叉表查詢

有三個表,資產,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.) 

期望的結果:

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

一些數據:

 
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

您正在使用什麼RDBMS? – Taryn

+0

(75,56,50)部署值從哪裏來?它們不會出現在您的數據中。 –

+0

這對我來說目前沒什麼意義 您是否有一些示例,瞭解您的表格中的內容,以及一些實際行... 表格之間必須有一些共同點作爲參考將它們鏈接在一起......如果你提供了這個細節,我會去看看它。 –

回答

40

這種類型的轉換被稱爲支點。你沒有指定你正在使用的數據庫,所以我將提供SQL Server和MySQL的答案。


SQL服務器:如果您正在使用SQL Server 2005+就可以實現PIVOT功能。

如果您有已知數量的值要轉換爲列,那麼您可以硬編碼查詢。

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; 

請參閱SQL Fiddle with Demo

但是,如果您有一個未知數的status值,那麼您將需要使用動態sql在運行時生成列的列表。

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) 

參見SQL Fiddle with Demo

這還可以使用具有的情況下表達的聚集函數寫爲:

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 

參見SQL Fiddle with Demo


MySQL的:此數據庫做es沒有支點函數,所以你將不得不使用聚合函數和CASE表達式。它也沒有窗口函數,所以你將不得不稍微改變查詢到以下幾點:

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; 

SQL Fiddle with Demo

然後,如果你需要在MySQL中的動態解決方案,你將不得不使用準備語句生成SQL字符串來執行:

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; 

SQL Fiddle with Demo

結果是所有查詢相同的兩個數據庫:

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

已知數字的SQL服務器工作得很好。使用未知的動態sql數字,我得到了一些錯誤: Msg 1038,Level 15,State 4,Line 15 對象或列名缺失或爲空。對於SELECT INTO語句,請確認每列都有一個名稱。對於其他語句,查找空的別名。別名定義爲「」或[]是不允許的。將別名更改爲有效的名稱。 – Sam

+1

@Sam你可以用你試圖運行的代碼編輯[SQL小提琴](http://www.sqlfiddle.com/#!3/d7915/7)嗎?將代碼放在右側面板中,然後執行sql。然後在這裏發表評論鏈接。 – Taryn

+0

我創建了表格,執行代碼並且一切正常。當我將它應用到我的數據庫時,出現錯誤。字段名稱是正確的。不知道爲什麼它不起作用。 – Sam