Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.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 server 2008 如何根据两个WHERE条件和两个计算计数字段将SQL查询结果拆分为列?_Sql Server 2008 - Fatal编程技术网

Sql server 2008 如何根据两个WHERE条件和两个计算计数字段将SQL查询结果拆分为列?

Sql server 2008 如何根据两个WHERE条件和两个计算计数字段将SQL查询结果拆分为列?,sql-server-2008,Sql Server 2008,我有以下(简化的)数据库模式: Persons: [Id] [Name] ------------------- 1 'Peter' 2 'John' 3 'Anna' Items: [Id] [ItemName] [ItemStatus] ------------------- 10 'Cake' 1 20 'Dog' 2 ItemDocuments: [Id] [ItemId] [DocumentName] [Date] ------------------- 101 10 'Ca

我有以下(简化的)数据库模式:

Persons:

[Id] [Name]
-------------------
1 'Peter'
2 'John'
3 'Anna'

Items:

[Id] [ItemName] [ItemStatus]
-------------------
10 'Cake' 1
20 'Dog' 2

ItemDocuments:

[Id] [ItemId] [DocumentName] [Date]
-------------------
101 10 'CakeDocument1' '2016-01-01 00:00:00'
201 20 'DogDocument1' '2016-02-02 00:00:00'
301 10 'CakeDocument2' '2016-03-03 00:00:00'
401 20 'DogDocument2' '2016-04-04 00:00:00'


DocumentProcessors:

[PersonId] [DocumentId]
-------------------
1 101
1 201
2 301
我还设置了一个SQL提琴:

关系逻辑如下:每个人都可以处理零个或无限多个ItemDocuments(多对多);每个ItemDocument只属于一个项目(一对多)。项目状态为1-活动,2-关闭

我需要的是一份满足以下要求的报告:

  • 对于Persons表中的每个人员,显示具有与此人相关的ItemDocuments的项目计数
  • 计数应按ItemStatus分为两列
  • 查询应该可以通过两个可选的日期周期进行过滤(在ItemDocuments.date字段上使用两个中间条件),并且项目计数也应该分为两个周期
  • 如果一个人没有分配任何ItemDocuments,那么它仍然应该显示在结果中,并且所有计数值都设置为0
  • 如果一个人对一个项目有多个ItemDocument,则该项目仍应仅计数一次
本质上,如果我将两个句点都使用为NULL(读取所有数据),结果应该是这样的:

虽然我可以分别为每个需求创建一个SQL查询,但我在理解如何将所有需求组合成一个查询时遇到了一个问题

例如,我可以使用

COUNT(CASE WHEN t.ItemStatus = 1 THEN 1 ELSE NULL END) AS Active, 
COUNT(CASE WHEN t.ItemStatus = 2 THEN 1 ELSE NULL END) AS Closed
我可以使用以下方法按两个时段进行筛选(使用MS SQL server规范中的max/min date常量,以避免可选时段日期为空)

但考虑到表之间的连接,如何将所有这些结合在一起呢

是否有任何技术、
join
或MS SQL Server特定的方法可以有效地做到这一点?

我的第一次尝试似乎可以按要求工作,但看起来像是多次丑陋的子查询重复:

DECLARE @start1 DATETIME, @start2 DATETIME, @end1 DATETIME, @end2 DATETIME

-- SET @start2 = '2017-01-01'

SELECT 
p.Name,
(SELECT COUNT(1)
    FROM Items i
    WHERE i.ItemStatus = 1 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start1, '1753-01-01') AND COALESCE(@end1, '9999-12-31')
        )
) AS Active1,

(SELECT COUNT(*)
    FROM Items i
    WHERE i.ItemStatus = 2 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start1, '1753-01-01') AND COALESCE(@end1, '9999-12-31')
        )
    ) AS Closed1,

(SELECT COUNT(1)
    FROM Items i
    WHERE i.ItemStatus = 1 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start2, '1753-01-01') AND COALESCE(@end2, '9999-12-31')
        )
) AS Active2,

(SELECT COUNT(*)
    FROM Items i
    WHERE i.ItemStatus = 2 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start2, '1753-01-01') AND COALESCE(@end2, '9999-12-31')
        )
    ) AS Closed2

FROM Persons p

我不确定我是否真的得到了你想要的,但你可以试试这个

WITH AllData AS
(
    SELECT p.Id AS PersonId
          ,p.Name AS Person
          ,id.Date AS DocDate
          ,id.DocumentName AS DocName
          ,i.ItemName AS ItemName
          ,i.ItemStatus AS ItemStatus
          ,CASE WHEN id.Date BETWEEN COALESCE(@start1, '1753-01-01') AND COALESCE(@end1, '9999-12-31') THEN 1 ELSE 0 END AS InPeriod1  
          ,CASE WHEN id.Date BETWEEN COALESCE(@start2, '1753-01-01') AND COALESCE(@end2, '9999-12-31') THEN 1 ELSE 0 END AS InPeriod2
    FROM Persons AS p
    LEFT JOIN DocumentProcessors AS dp ON p.Id=dp.PersonId
    LEFT JOIN ItemDocuments AS id ON dp.DocumentId=id.Id
    LEFT JOIN Items AS i ON id.ItemId=i.Id
)
SELECT PersonID 
      ,Person
      ,COUNT(CASE WHEN ItemStatus = 1 AND InPeriod1 = 1 THEN 1 ELSE NULL END) AS ActiveIn1
      ,COUNT(CASE WHEN ItemStatus = 2 AND InPeriod1 = 1  THEN 1 ELSE NULL END) AS ClosedIn1
      ,COUNT(CASE WHEN ItemStatus = 1 AND InPeriod2 = 1 THEN 1 ELSE NULL END) AS ActiveIn2
      ,COUNT(CASE WHEN ItemStatus = 2 AND InPeriod2 = 1  THEN 1 ELSE NULL END) AS ClosedIn2
FROM AllData
GROUP BY PersonID,Person

这有一个好问题,样本数据(甚至是一把小提琴!),自己的努力,预期的产出,清晰的解释(尽管仍然很难摸索)。。。投票给upI我试图通过使用CTE避免您丑陋的子查询重复…谢谢,它几乎可以工作。只有一个问题-目前它计数的文件,但我需要项目计数。我的意思是-如果一个项目为一个人分配了多个ItemDocuments,那么在每个活动/关闭期间,该项目仍应仅在每个期间计算一次。我想,这需要更深入的分组/CTE,可能需要一些总和而不是计数。尝试将CTE中的列减少到真正需要的列(没有
DocName
DocDate
并将CTE更改为
SELECT DISTINCT
。应该足够了……好的,我刚刚尝试修改了您的CTE,终于让它工作了。@JustAMartin,太好了,很高兴来到这里!编码愉快!
DECLARE @start1 DATETIME, @start2 DATETIME, @end1 DATETIME, @end2 DATETIME

-- SET @start2 = '2017-01-01'

SELECT 
p.Name,
(SELECT COUNT(1)
    FROM Items i
    WHERE i.ItemStatus = 1 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start1, '1753-01-01') AND COALESCE(@end1, '9999-12-31')
        )
) AS Active1,

(SELECT COUNT(*)
    FROM Items i
    WHERE i.ItemStatus = 2 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start1, '1753-01-01') AND COALESCE(@end1, '9999-12-31')
        )
    ) AS Closed1,

(SELECT COUNT(1)
    FROM Items i
    WHERE i.ItemStatus = 1 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start2, '1753-01-01') AND COALESCE(@end2, '9999-12-31')
        )
) AS Active2,

(SELECT COUNT(*)
    FROM Items i
    WHERE i.ItemStatus = 2 AND EXISTS(
        SELECT 1 
        FROM DocumentProcessors AS dcp
        INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
        WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
        AND idc.Date BETWEEN COALESCE(@start2, '1753-01-01') AND COALESCE(@end2, '9999-12-31')
        )
    ) AS Closed2

FROM Persons p
WITH AllData AS
(
    SELECT p.Id AS PersonId
          ,p.Name AS Person
          ,id.Date AS DocDate
          ,id.DocumentName AS DocName
          ,i.ItemName AS ItemName
          ,i.ItemStatus AS ItemStatus
          ,CASE WHEN id.Date BETWEEN COALESCE(@start1, '1753-01-01') AND COALESCE(@end1, '9999-12-31') THEN 1 ELSE 0 END AS InPeriod1  
          ,CASE WHEN id.Date BETWEEN COALESCE(@start2, '1753-01-01') AND COALESCE(@end2, '9999-12-31') THEN 1 ELSE 0 END AS InPeriod2
    FROM Persons AS p
    LEFT JOIN DocumentProcessors AS dp ON p.Id=dp.PersonId
    LEFT JOIN ItemDocuments AS id ON dp.DocumentId=id.Id
    LEFT JOIN Items AS i ON id.ItemId=i.Id
)
SELECT PersonID 
      ,Person
      ,COUNT(CASE WHEN ItemStatus = 1 AND InPeriod1 = 1 THEN 1 ELSE NULL END) AS ActiveIn1
      ,COUNT(CASE WHEN ItemStatus = 2 AND InPeriod1 = 1  THEN 1 ELSE NULL END) AS ClosedIn1
      ,COUNT(CASE WHEN ItemStatus = 1 AND InPeriod2 = 1 THEN 1 ELSE NULL END) AS ActiveIn2
      ,COUNT(CASE WHEN ItemStatus = 2 AND InPeriod2 = 1  THEN 1 ELSE NULL END) AS ClosedIn2
FROM AllData
GROUP BY PersonID,Person