Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 我需要知道如何创建交叉表查询_Sql_Pivot_Crosstab - Fatal编程技术网

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

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 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 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地位表有一个空白状态的记录,我把空白状态改为“没有状态”,现在它都工作了。非常感谢。