Sql 我需要知道如何创建交叉表查询
我需要帮助创建以下结果。我想到了一个sql pivot,但我不知道如何使用它。我看了几个例子,没能想出一个解决办法。关于如何实现这一点的任何其他想法也是受欢迎的。必须动态生成状态列 有三个表:资产、资产类型、资产状态 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.) 表:资产 阿塞蒂德国际酒店 assettag varchar(25) 资产类型int 资产状态int 表:资产类型 id int typename varchar(20)(例如:台式机、笔记本电脑、服务器等) 表:资产状态 id int statusname varchar(20)(例如:已部署、库存、已装运等) 预期结果: AssetType Total Deployed Inventory Shipped ... ----------------------------------------------------------- Desktop 100 75 20 5 ... Laptop 75 56 19 1 ... Server 60 50 10 0 ... 资产类型已部署的已装运库存总量。。。 ----------------------------------------------------------- 桌面100 75 20 5。。。 笔记本电脑7556191。。。 服务器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 资产表: 1,1234,1,1 2,1233,1,2 3,hol3421,2,3 4,svr1234,3,1 资产类型表: 1、桌面 2、笔记本电脑 3、服务器端 资产状态表: 1、部署 2、存货 3,已装运Sql 我需要知道如何创建交叉表查询,sql,pivot,crosstab,Sql,Pivot,Crosstab,我需要帮助创建以下结果。我想到了一个sql pivot,但我不知道如何使用它。我看了几个例子,没能想出一个解决办法。关于如何实现这一点的任何其他想法也是受欢迎的。必须动态生成状态列 有三个表:资产、资产类型、资产状态 Table: assets assetid int assettag varchar(25) assettype int assetstatus int Table: assettypes id int typename varchar(20
这种类型的转换称为枢轴。您没有指定正在使用的数据库,因此我将提供SQL Server和MySQL的答案
SQL Server:如果您使用的是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;
看
但是,如果有数量未知的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)
看
这也可以使用带有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 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
看
MySQL:此数据库没有pivot函数,因此必须使用聚合函数和
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;
看
然后,如果您需要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;
看
两个数据库中的所有查询的结果相同:
| TYPENAME | TOTAL | DEPLOYED | INVENTORY | SHIPPED |
-----------------------------------------------------
| Desktop | 2 | 1 | 1 | 0 |
| Laptop | 1 | 0 | 0 | 1 |
| Server | 1 | 1 | 0 | 0 |
使用非pivot兼容的DBMS(绝对数据库),我更成功地使用了以下SQL交叉表等效语句:
SELECT
sub.TypeName
, SUM(sub.[Count]) AS "Total"
, SUM(CASE WHEN AssetStatus='1' THEN sub.[Count] ELSE 0 END) AS "Deployed"
, SUM(CASE WHEN AssetStatus='2' THEN sub.[Count] ELSE 0 END) AS "Inventory"
, SUM(CASE WHEN AssetStatus='3' THEN sub.[Count] ELSE 0 END) AS "Shipped"
FROM
(
SELECT
t.TypeName
, AssetStatus
, COUNT(AssetID) AS "Count"
FROM
Assets
JOIN AssetTypes t ON t.ID = AssetType
JOIN AssetStatus s ON s.ID = AssetStatus
GROUP BY t.TypeName, AssetStatus, s.StatusName
) sub
GROUP BY sub.TypeName
;
当我意识到这段代码(上面的)不适用于MySQL时,我对下面的代码进行了修改,使其在MySQL中的执行效果与在我当前的绝对数据库中一样好。原因是特定的NULL处理避免了dBase、Paradox以及绝对数据库的陷阱,它们慷慨地接受了主流数据库中不接受的计数(NULL)=0。
因此,相信这将在大多数数据库中很好地执行(处理案例…)这是我的改编代码:
SELECT
sub.TypeName
, SUM(sub.AssetCase) AS "Total"
, SUM(CASE WHEN sub.StatusName = 'Deployed' THEN sub.AssetCase ELSE 0 END) AS "Deployed"
, SUM(CASE WHEN sub.StatusName = 'Inventory' THEN sub.AssetCase ELSE 0 END) AS "Inventory"
, SUM(CASE WHEN sub.StatusName = 'Shipped' THEN sub.AssetCase ELSE 0 END) AS "Shipped"
FROM
(
SELECT
c.TypeName
, c.StatusName
, CASE WHEN a.AssetID IS NULL THEN 0 ELSE 1 END AS "AssetCase"
FROM
(
SELECT
t.ID AS tID
, t.TypeName
, s.ID AS sID
, s.StatusName
FROM
AssetTypes t, AssetStatus s
) c
LEFT JOIN Assets a
ON a.AssetType = c.tID AND a.AssetStatus = c.sID
) sub
GROUP BY
sub.TypeName
;
致意
尼尔斯·克纳贝(Niels Knabe)这对我来说现在没有太多意义。你有一些例子说明你的表格中有什么,一些实际的行吗。。。表之间必须有一些共同点,作为将它们联系在一起的参考点。。。如果您提供了详细信息,我将尝试一下。您使用的RDBMS是什么?assettypes.id上的assets.assettype,assets.assetstatus上的assets.assetstatus(75、56、50)部署值来自何处?它们不会出现在您的数据中。@EricS这些是每种类型的总计数,但数字与示例数据不匹配。使用已知数字的SQL server工作得很好。对于一个未知数字的动态sql,我得到了两个错误:Msg 1038,级别15,状态4,第15行对象或列名丢失或为空。对于SELECT INTO语句,请验证每个列都有名称。对于其他语句,请查找空别名。不允许使用定义为“”或[]的别名。将别名更改为有效名称。@Sam您能用您试图运行的代码编辑别名吗?将代码放在右侧面板中,然后执行sql。然后在这里发布一个注释中的链接。我创建了表,执行了代码,一切都很好。当我把它应用到我的数据库时,我得到一个错误。字段名是正确的。不知道为什么不起作用。这是你的代码。工作很好,但不是在我的实际DB,它有完全相同的字段。好,我发现了这个问题,我的AsStEt地位表有一个空白状态的记录,我把空白状态改为“没有状态”,现在它都工作了。非常感谢。