使用 PIVOT 格式化多个 SELECT 语句
Posted
技术标签:
【中文标题】使用 PIVOT 格式化多个 SELECT 语句【英文标题】:Formatting multiple SELECT statements with PIVOT 【发布时间】:2017-10-24 11:43:43 【问题描述】:目前有一个脚本可以合并十几个 SELECT 语句,其中两个示例以及结果示例如下所示。
DECLARE @Age TABLE (name VARCHAR(30), total FLOAT, percentage FLOAT)
INSERT INTO @Age
SELECT '0-18', (SELECT COUNT(*) FROM tblPerson p
INNER JOIN tblClient c ON c.intPersonID = p.intPersonID
WHERE ISNULL(dbo.fncReportClient_Age(p.dteBirthdate, GETDATE()), '') >= 0 AND ISNULL(dbo.fncReportClient_Age(p.dteBirthdate, GETDATE()), '') <= 18), ''
UPDATE @Age
SET percentage = ROUND((SELECT total FROM @Age WHERE name = '0-18')/(SELECT SUM(total) FROM @Age) * 100, 2)
FROM @Age
WHERE name = '0-18'
Etc.
SELECT
g.nvhGenderName,
COUNT(*),
ROUND(COUNT(*) * 1.0 / SUM(COUNT(*)) OVER () * 100, 2)
FROM
tblClient c
LEFT JOIN tblPerson p ON p.intPersonID = c.intPersonID
LEFT JOIN tblGender g ON g.intGenderID = p.intGenderID
GROUP BY g.nvhGenderName
UNION ALL
SELECT * FROM @Age
结果示例如下:
Name | Total | % |
---------------------------------
Male | 6514 | 60.32 |
Female | 4285 | 39.68 |
0-18 | 279 | 1.58 |
19-24 | 1748 | 9.93 |
25-34 | 5423 | 30.80 |
35-64 | 9546 | 54.21 |
65+ | 614 | 3.50 |
我想水平显示这些结果,而不是垂直显示,我认为 PIVOT 可以做到这一点,但从未真正使用过它们。我希望如何显示数据的示例如下所示:
Gender | Total | % | Age | Total | % |
-------------------------------------------------------------
Male | 6514 | 60.32 | 0-18 | 279 | 1.58 |
Female | 4285 | 39.68 | 19-24 | 1748 | 9.93 |
| | | 25-34 | 5423 | 30.80 |
| | | 35-64 | 9546 | 54.21 |
| | | 65+ | 614 | 3.50 |
特别是我不确定如何使用数据透视来组合需要它的多个 (12) SELECT 语句。
任何有关如何格式化的帮助将不胜感激。
【问题讨论】:
您想要实现的只是两个枢轴结果集,一个超过性别,一个超过年龄,它们只是“粘合”在一起。您确定这是您想要呈现数据的方式吗?如果是这样 - 也许最好将两个查询结果传递给可视化引擎并在那里组合它们? SQL 不是为“演示”而设计的(尤其是“漂亮的演示”) 您将如何将此报告提供给您的用户?在 html 中?使用 php? (如果是这样,那么为此使用 PHP/HTML) 【参考方案1】:虽然我认为布局确实应该在其他地方实现,但以下可能对您有用。显然我不能这样测试它,没有在这里测试的好处:
WITH myCTE AS (
SELECT
COUNT(CASE WHEN oa.age >= 0 AND oa.age < 19 THEN p.intPersonID ELSE NULL END) c0019
, COUNT(CASE WHEN oa.age >= 19 AND oa.age < 25 THEN p.intPersonID ELSE NULL END) c1925
, COUNT(CASE WHEN oa.age >= 25 AND oa.age < 35 THEN p.intPersonID ELSE NULL END) c2535
, COUNT(CASE WHEN oa.age >= 35 AND oa.age < 65 THEN p.intPersonID ELSE NULL END) c3565
, COUNT(CASE WHEN oa.age >= 65 THEN p.intPersonID ELSE NULL END) c65on
, COUNT(CASE WHEN g.nvhGenderName = 'Male' THEN p.intPersonID ELSE NULL END) cmale
, COUNT(CASE WHEN g.nvhGenderName = 'Female' THEN p.intPersonID ELSE NULL END) cfemale
, COUNT(*) ctotal
FROM tblClient c
LEFT JOIN tblPerson p ON p.intPersonID = c.intPersonID
OUTER APPLY (
SELECT
dbo.fncReportClient_Age(p.dteBirthdate) AS age
) AS oa
LEFT JOIN tblGender g ON g.intGenderID = p.intGenderID
)
SELECT
ca.Gender, ca.Total2, ca.Pct, ca.Age, ca.Total2, ca.Pct2
FROM myCTE
CROSS APPLY (
VALUES
(1, 'Male' , t.cmale , (cmale * 100.0 / ctotal), '0-18', c0019, (c0019 * 100.0 / ctotal))
, (2, 'Female',t.cfemale,(cfemale * 100.0 / ctotal), '19-24', c1925, (c1925 * 100.0 / ctotal))
, (3, NULL,NULL,NULL, '25-34', c2535, (c2535 * 100.0 / ctotal))
, (4, NULL,NULL,NULL, '35-64', c3565, (c3565 * 100.0 / ctotal))
, (5, NULL,NULL,NULL, '65+' , c65on, (c65on * 100.0 / ctotal))
) AS ca (rn, Gender, Total2, Pct, Age, Total2, Pct2)
ORDER BY ca.rn
;
上面查询的第二部分(下部)使用unpivoting
数据的技术,将cross apply
与values
结合起来,这允许我们逐行“布局”所需最终结果的每一行。
使用CTE
(上面查询的上半部分)不是必需的,它可以被移到子查询中,但CTE
内的查询应该首先独立试用,它应该产生所有您在一次数据中需要的数字(假设性别表不会干扰计数结果)。请注意,使用count(case expression here)
消除了对多个单独查询的需要。顺便说一句,我怀疑您不需要左连接,如果这是真的,您还可以将外部应用更改为交叉应用。请注意,apply 用于执行您的函数,通过这样做,我们可以在查询的其余部分中通过别名引用该函数的结果(我使用“age”作为别名)。
我不确定为什么在计算人员表时涉及客户表。我怀疑不需要。如果我的怀疑是正确的,CTE 详细信息可以替换为:
SELECT
COUNT(CASE WHEN oa.age >= 0 AND oa.age < 19 THEN 1 ELSE NULL END) c0019
, COUNT(CASE WHEN oa.age >= 19 AND oa.age < 25 THEN 1 ELSE NULL END) c1925
, COUNT(CASE WHEN oa.age >= 25 AND oa.age < 35 THEN 1 ELSE NULL END) c2535
, COUNT(CASE WHEN oa.age >= 35 AND oa.age < 65 THEN 1 ELSE NULL END) c3565
, COUNT(CASE WHEN oa.age >= 65 THEN 1 ELSE NULL END) c65on
, COUNT(CASE WHEN g.nvhGenderName = 'Male' THEN 1 ELSE NULL END) cmale
, COUNT(CASE WHEN g.nvhGenderName = 'Feale' THEN 1 ELSE NULL END) cfemale
, COUNT(*) ctotal
FROM tblPerson p
CROSS APPLY (
SELECT
dbo.fncReportClient_Age(p.dteBirthdate) AS age
) AS oa
INNER JOIN tblGender g ON g.intGenderID = p.intGenderID
【讨论】:
顺便说一句,这个建议是“使用 unpivot 格式化单个数据透视查询”,因此与您的问题标题完全不同:) 您的问题现在解决了吗?你对这个答案还有疑问吗?要接受并回答“Click the Tick”以获取更多信息,请参阅help/accepting【参考方案2】:Create VIEW view_name AS
SELECT * FROM (
select ColumnName1,ColumnName2 from TableName
) as s
PIVOT (
Sum(ColumnName2)
FOR ColumnName3 in ("Row1","Row2","Row3"
)
) As pvt
DROP VIEW view_name;
Select * from view_name
【讨论】:
这个答案是否真正解决了 OP 的要求并不明显。这是很好的清晰代码,但它有什么作用?这个问题已经有一年多了,所以假设 OP 已经继续前进并且不再关心了。在这种情况下,除非答案发生了变化(新技术),或者您可以在现有答案的基础上大幅改进(附有详细解释),否则可能不值得回答。以上是关于使用 PIVOT 格式化多个 SELECT 语句的主要内容,如果未能解决你的问题,请参考以下文章
是否可以查询 SQL Pivot 报告并将输出连接到另一个 select 语句中?