使用 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 applyvalues 结合起来,这允许我们逐行“布局”所需最终结果的每一行。

使用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 语句中?

Excel 条件格式 - 多个“开头”语句

如何在objc中使用带有多个where值的select语句?

SQL Server - 同一列上的多个 PIVOT

操作LINQ 中 怎么 Select Tuple 类的值?

交叉报表sql语句使用