如何将行旋转到列(自定义旋转)

Posted

技术标签:

【中文标题】如何将行旋转到列(自定义旋转)【英文标题】: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代码吗?

【问题讨论】:

【参考方案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;

【讨论】:

以上是关于如何将行旋转到列(自定义旋转)的主要内容,如果未能解决你的问题,请参考以下文章

SQL - 将行旋转到列并采用笛卡尔积

PIVOT 动态地将行旋转到列中

如何在自定义 UICollectionView 的旋转后触发 drawrect?

如何在 Android 屏幕旋转上保存自定义 ArrayList?

如何在 iOS 上旋转自定义启动画面?

iOS5 自定义窗口旋转问题