sql – 我需要知道如何创建交叉表查询

我需要帮助创建以下结果。我想到了一个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 Fiddle with Demo

但是,如果您具有未知数量的状态值,则需要使用动态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)

SQL Fiddle with Demo

这也可以使用带有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

SQL Fiddle with Demo

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;

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 `','`'
    )
  ) 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;

SQL Fiddle with Demo

两个数据库中的所有查询的结果都相同:

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

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 'EastRiver' 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...