2013-03-30 109 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