【问题标题】:How to pivot rows into columns (custom pivoting)如何将行旋转到列(自定义旋转)
【发布时间】:2011-03-08 10:37:46
【问题描述】:

我有一个类似下面的 Sql 数据库表:

Day   Period    Subject

Mon   1         Ch
Mon   2         Ph
Mon   3         Mth
Mon   4         CS
Mon   5         Lab1
Mon   6         Lab2
Mon   7         Lab3
Tue   1         Ph
Tue   2         Ele
Tue   3         Hu
Tue   4         Ph
Tue   5         En
Tue   6         CS2
Tue   7         Mth

我希望它显示如下:交叉表或枢轴的种类

Day   P1   P2   P3   P4   P5   P6   P7

Mon   Ch   Ph   Mth  CS2  Lab1 Lab2 Lab3
Tue   Ph   Ele  Hu   Ph   En   CS2  Mth

理想的方法是什么?谁能给我看看Sql代码吗?

【问题讨论】:

    标签: sql tsql pivot


    【解决方案1】:

    你可以用 PIVOT 函数来做,但我更喜欢老派的方法:

    SELECT
        dy,
        MAX(CASE WHEN period = 1 THEN subj ELSE NULL END) AS P1,
        MAX(CASE WHEN period = 2 THEN subj ELSE NULL END) AS P2,
        MAX(CASE WHEN period = 3 THEN subj ELSE NULL END) AS P3,
        MAX(CASE WHEN period = 4 THEN subj ELSE NULL END) AS P4,
        MAX(CASE WHEN period = 5 THEN subj ELSE NULL END) AS P5,
        MAX(CASE WHEN period = 6 THEN subj ELSE NULL END) AS P6,
        MAX(CASE WHEN period = 7 THEN subj ELSE NULL END) AS P7
    FROM
        Classes
    GROUP BY
        dy
    ORDER BY
        CASE dy
            WHEN 'Mon' THEN 1
            WHEN 'Tue' THEN 2
            WHEN 'Wed' THEN 3
            WHEN 'Thu' THEN 4
            WHEN 'Fri' THEN 5
            WHEN 'Sat' THEN 6
            WHEN 'Sun' THEN 7
            ELSE 8
        END
    
    • 我更改了一些列名以避免保留字

    【讨论】:

    • +1:你速度更快,并且有 ORDER BY。所以我要补充一点:如您所见,动态列将需要使用动态 SQL。有 ANSI PIVOT 语法,但仅在 SQL Server 2005+ 和 Oracle 11g 上受支持。
    • 我目前正在 SQLite 中执行此操作,但它似乎是一项相当密集的操作。有谁知道是否有更高效的解决方案?
    • 我不相信 SQLite 有任何内置的数据透视函数,所以这可能就是你所坚持的。虽然我不使用 SQLite,所以也许对特定 SQL 供应商有更多经验的人会有更好的主意。
    【解决方案2】:

    以防万一您确实想要新的学校方法。 (Pivot 语句应该在 SQL2005+ 中工作,VALUES 位仅适用于示例数据 SQL2008)

    WITH ExampleData AS
    (
    SELECT X.*
      FROM (VALUES  
    ('Mon', 1, 'Ch'),
    ('Mon', 2, 'Ph'),
    ('Mon', 3, 'Mth'),
    ('Mon', 4, 'CS'),
    ('Mon', 5, 'Lab1'),
    ('Mon', 6, 'Lab2'),
    ('Mon', 7, 'Lab3'),
    ('Tue', 1, 'Ph'),
    ('Tue', 2, 'Ele'),
    ('Tue', 3, 'Hu'),
    ('Tue', 4, 'Ph'),
    ('Tue', 5, 'En'),
    ('Tue', 6, 'CS2'),
    ('Tue', 7, 'Mth')
    ) AS X (Day,   Period,    Subject)
    )
    
    SELECT Day, [1] AS P1, [2] AS P2,[3] AS P3, [4] AS P4, [5] AS P5,[6] AS P6,[7] AS P7
    FROM ExampleData
    PIVOT  
    (  
    Max(Subject)  
    FOR Period IN ([1], [2],[3],[4], [5],[6], [7])  
    ) AS PivotTable; 
    

    结果

    Day  P1   P2   P3   P4   P5   P6   P7
    ---- ---- ---- ---- ---- ---- ---- ----
    Mon  Ch   Ph   Mth  CS   Lab1 Lab2 Lab3
    Tue  Ph   Ele  Hu   Ph   En   CS2  Mth
    

    【讨论】:

    • 为什么选择 Max(主题)?这是因为 PIVOT 必须采用聚合函数吗?
    • @JBRWilkinson 是的。确切地。日/期间组合应该只有一个匹配记录,因此“MIN”同样有效。
    【解决方案3】:

    你可以试试……

    SELECT DISTINCT Day,
           (SELECT Subject
                FROM my_table mt2
                WHERE mt2.Day = mt.Day AND
                      Period  = 1) AS P1,
           (SELECT Subject
                FROM my_table mt2
                WHERE mt2.Day = mt.Day AND
                      Period  = 2) AS P2,
       .
       .
       etc
       .
       .
       .
       (SELECT Subject
            FROM my_table mt2
            WHERE mt2.Day = mt.Day AND
                  Period  = 7) AS P7
    FROM my_table mt;
    

    但我不能说我非常喜欢它。不过,总比没有好。

    【讨论】:

      【解决方案4】:

      使用交叉应用以逗号分隔格式在单个列中获取所有值。而不是“7”不同的列。以下查询可用于任何列-> 行映射

      SELECT DISTINCT Day, [DerivedColumn] FROM <Table> A CROSS APPLY ( SELECT Period + ',' FROM <Table> B WHERE A.Day = B.Day Order By Period FOR XML PATH('') ) AS C (DerivedColumn)
      

      您将在 Mon 的一列中获得 [Ch,Ph,Mth,CS2,Lab1,Lab2,Lab3] 等...您可以将其用作查询任何特定日期的表格。

      希望对你有帮助

      【讨论】:

        【解决方案5】:
        DECLARE @TIMETABLE TABLE (
            [Day]       CHAR(3),
            [Period]    TINYINT,
            [Subject]   CHAR(5)
        )
        INSERT INTO @TIMETABLE([Day], [Period], [Subject])
        VALUES
            ('Mon', 1, 'Ch'),
            ('Mon', 2, 'Ph'),
            ('Mon', 3, 'Mth'),
            ('Mon', 4, 'CS'),
            ('Mon', 5, 'Lab1'),
            ('Mon', 6, 'Lab2'),
            ('Mon', 7, 'Lab3'),
            ('Tue', 1, 'Ph'),
            ('Tue', 2, 'Ele'),
            ('Tue', 3, 'Hu'),
            ('Tue', 4, 'Ph'),
            ('Tue', 5, 'En'),
            ('Tue', 6, 'CS2'),
            ('Tue', 7, 'Mth')
        
        SELECT 
            [Day],
            MAX(CASE [Period] WHEN 1 THEN [Subject] END) AS P1,
            MAX(CASE [Period] WHEN 2 THEN [Subject] END) AS P2,
            MAX(CASE [Period] WHEN 3 THEN [Subject] END) AS P3,
            MAX(CASE [Period] WHEN 4 THEN [Subject] END) AS P4,
            MAX(CASE [Period] WHEN 5 THEN [Subject] END) AS P5,
            MAX(CASE [Period] WHEN 6 THEN [Subject] END) AS P6,
            MAX(CASE [Period] WHEN 7 THEN [Subject] END) AS P7
        FROM @TIMETABLE
        GROUP BY [Day]
        

        【讨论】:

          【解决方案6】:
          with pivot_data as
          (
          select [day], -- groping column
          period, -- spreading column
          subject -- aggreate column
          from pivot_tb
          )
          select [day],  [1] AS P1, [2] AS P2,[3] AS P3, [4] AS P4, [5] AS P5,[6] AS P6,[7] AS P7
          from pivot_data
          pivot ( max(subject) for period in ([1], [2],[3],[4], [5],[6], [7]) ) as p;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-01-29
            • 2016-06-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-28
            • 2012-10-31
            相关资源
            最近更新 更多