我需要帮助创建以下结果。我想到了一个sql pivot,但我不知道如何使用它。看了几个例子,无法想出解决方案。关于如何实现这一目标的任何其他想法也是受欢迎的。必须动态生成状态列。
有三个表,资产,资产类型,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,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
解决方法
这种类型的转换称为枢轴。您没有指定您正在使用的数据库,因此我将为sql Server和MysqL提供答案。
sql Server:如果您使用的是sql Server 2005,则可以实现PIVOT功能。
select typename,total,Deployed,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,shipped) ) piv;
但是,如果您具有未知数量的状态值,则需要使用动态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)'),'') set @query = 'SELECT typename,' + @cols + ' from ( select count(*) over(partition by t.typename) total,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)
这也可以使用带有case表达式的聚合函数编写:
select typename,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,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
MysqL:这个数据库没有pivot函数,所以你必须使用aggregate函数和CASE表达式。它也没有窗口函数,因此您必须稍微更改查询到以下内容:
select typename,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;
然后,如果您需要MysqL中的动态解决方案,则必须使用预准备语句来生成要执行的sql字符串:
SET @sql = NULL; SELECT GROUP_CONCAT(DISTINCT CONCAT( 'sum(CASE WHEN statusname = ''',statusname,''' THEN 1 else 0 END) AS `','`' ) ) INTO @sql FROM assetstatus; SET @sql = CONCAT('SELECT typename,',@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;
| TYPENAME | TOTAL | DEPLOYED | INVENTORY | SHIPPED | ----------------------------------------------------- | Desktop | 2 | 1 | 1 | 0 | | Laptop | 1 | 0 | 0 | 1 | | Server | 1 | 1 | 0 | 0 |