我有以下的(简化)的数据库模式:如何根据两个WHERE条件和两个计算的COUNT字段将SQL查询结果拆分为列?
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拨弄玩:http://www.sqlfiddle.com/#!3/e6082
关系逻辑是这样的:每个人都可以在零或工作无限数量的ItemDocuments(多对多);每个ItemDocument只属于一个Item(一对多)。项目具有状态1 - 活动,2 - 关闭
我需要的是满足下列要求的报告:
- 每个人在个人表,有关于这个人ItemDocuments项目显示计数
- 计数应在两列由ItemStatus拆分
- 查询应通过两个可选的日期时间(使用两者之间的ItemDocuments.Date现场条件)和项目数是过滤也应分为两个阶段
- 如果一个人没有分配任何ItemDocuments,它仍然应该在结果中显示所有计数值设置为0
- 如果一个人有一个以上ItemDocument的项目,项目仍应该只计算一次
从本质上讲,这里的结果应该如何看就像如果我用这两个时期为NULL(读取所有数据):
[PersonName] [Active Items for period 1] [Closed Items for period 1] [Active Items for period 2] [Closed Items for period 2]
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
'Peter' 1 1 1 1
'John' 1 0 1 0
'Anna' 0 0 0 0
虽然我可以单独为每个需求的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
分裂ItemStatus计数和我可以由两个时期筛选(最大/从MS SQL服务器规范分钟日期常量,以避免任意周期的日期的NULL)使用
between coalesce(@start1, '1753-01-01') and coalesce(@end1, '9999-12-31')
between coalesce(@start2, '1753-01-01') and coalesce(@end2, '9999-12-31')
但如何将所有这些结合在一起考虑表之间的JOIN?
是否有任何技术,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
具有所有一个很好的问题应该有,样本数据(即使是小提琴!),自己的努力,预计输出,清晰的解释(allthough它仍然很难神交)...投票起来 – Shnugo
我试图避免你的*丑陋的子查询重复*使用a CTE ... – Shnugo