【问题标题】:Rows to Columns (Pivot?)行到列(枢轴?)
【发布时间】:2016-05-22 06:35:23
【问题描述】:

我有一个结构如下的表:

cpf         ddd phone       type    origin
11111111111 83  81021111    M       SERASA
11111111111 83  87472222    M       SERASA
11111111111 83  81023333    M       TRANSUNION
11111111111 83  88724444    M       TRANSUNION
11111111111 83  87475555    M       TRANSUNION
22222222222 43  36271111    F       SERASA
22222222222 44  36272222    F       SERASA
22222222222 43  36273333    F       TRANSUNION
22222222222 43  36284444    F       TRANSUNION
33333333333 51  51811111    F       SERASA
33333333333 51  56212222    F       SERASA
33333333333 51  96213333    M       SERASA

我需要“去规范化”这个表,所以每个 CPF 将只有一行,类似这样:

CPF         DDD_1 PHONE_1   TYPE_1 ORIGIN_1 DDD_2 PHONE_2   TYPE_2 ORIGIN_2 DDD_3 PHONE_3   TYPE_3  ORIGIN_3    DDD_4 PHONE_4   TYPE_4  ORIGIN_4    DDD_5   PHONE_5  TYPE_5 ORIGIN_5
11111111111 83    81021111  M      SERASA   83    87472222  M      SERASA   83    81023333  M       TRANSUNION  83    88724444  M       TRANSUNION  83      8747555  M      TRANSUNION
22222222222 43    36271111  F      SERASA   44    36272222  F      SERASA   43    36273333  F       TRANSUNION  43    36274444  F       TRANSUNION  NULL    NULL     NULL   NULL
33333333333 51    51811111  F      SERASA   51    56212222  F      SERASA   51    96213333  M       SERASA      NULL  NULL      NULL    NULL        NULL    NULL     NULL   NULL

一位队友建议使用Pivot + Unpivot,但我没有找到如何使用它,因为我没有定义列的字段,也没有聚合(sum,count等)。

我使用与表本身连接的少量数据(100 行),但由于数据量(表有 3300 万行),此解决方案不起作用。

现在我被卡住了,任何帮助将不胜感激。

【问题讨论】:

  • “我没有定义列的字段”这是第一步 - 使用 ROWNUM 创建一个查询,该查询标识每个 cpf 组中的行号。然后旋转会容易得多
  • 我更喜欢交叉表而不是原生枢轴。我发现语法不那么迟钝,性能通常更好一点。这是一篇关于交叉表的精彩文章。 sqlservercentral.com/articles/T-SQL/63681 如果你需要使这个动态,你可以使用动态交叉表。 sqlservercentral.com/articles/Crosstab/65048

标签: sql sql-server tsql pivot sql-server-2014


【解决方案1】:

如果您自己动手,问题更有可能得到答案。 SO 将帮助您解决代码错误和问题,但不是代码编写服务。

这就是说;碰巧我有一个工作示例...

该示例使用您的示例记录,包装在一个表变量中。表变量使共享数据变得更加容易,SQL FiddleStack Data Exchange 等服务也是如此。如果您让社区更容易帮助您,您更有可能得到答案。

示例数据

/* I've used a table variable to make 
 * sharing the example data easier.
 */
DECLARE @Table TABLE
    (
        cpf         VARCHAR(11),
        ddd         INT,
        phone       VARCHAR(10),
        [type]      VARCHAR(1),
        origin      VARCHAR(10)
    )
;

-- Demo values taken from OP.
INSERT INTO @Table
    (
        cpf,
        ddd,
        phone,
        [type],
        origin
    )
VALUES
    ('11111111111', 83,  '87472222',    'M',   'SERASA'),
    ('11111111111', 83,  '81023333',    'M',   'TRANSUNION'),
    ('11111111111', 83,  '88724444',    'M',   'TRANSUNION'),
    ('11111111111', 83,  '87475555',    'M',   'TRANSUNION'),
    ('22222222222', 43,  '36271111',    'F',   'SERASA'),
    ('22222222222', 44,  '36272222',    'F',   'SERASA'),
    ('22222222222', 43,  '36273333',    'F',   'TRANSUNION'),
    ('22222222222', 43,  '36284444',    'F',   'TRANSUNION'),
    ('33333333333', 51,  '51811111',    'F',   'SERASA'),
    ('33333333333', 51,  '56212222',    'F',   'SERASA'),
    ('33333333333', 51,  '96213333',    'M',   'SERASA')
;

为了将您的行移动到列中,我对记录进行了排序。使用ROW_NUMBER,我为每个cpf 的第一条记录分配了1,第二条记录分配了2,依此类推。为了避免无休止地重新输入 ROW_NUMBER(),我将结果变成了 common table expression。 CTE 是创建临时、可重用数据集的好方法。最后一步是将编号为 1 的记录移动到第一组列中,将编号为 2 的记录移动到第二组列中,依此类推。

将行移入列可能需要更详细的解释。我已经使用CASE expressions 根据 ROW_NUMBER() 返回的序列号有条件地移动值。然后我GROUPED cpf 的结果。用 MAX 包装每个 case 表达式会返回所需的结果。

将行分成列

/* The records from each cdp are numbered.
 * The numbering is used to pivot the rows into
 * columns.
 */
WITH Sequenced AS
    (
        -- This CTE numbers the records for each cpf.
        SELECT
            ROW_NUMBER() OVER (PARTITION BY cpf ORDER BY phone) AS rn,
            *
        FROM
            @Table
    )
SELECT
    cpf,
    -- First set of columns.
    MAX(CASE WHEN rn = 1 THEN ddd       ELSE NULL END)  AS ddd_1,
    MAX(CASE WHEN rn = 1 THEN phone     ELSE NULL END)  AS phone_1,
    MAX(CASE WHEN rn = 1 THEN [type]    ELSE NULL END)  AS type_1,
    MAX(CASE WHEN rn = 1 THEN origin    ELSE NULL END)  AS origin_1,

    -- Second set of columns.
    MAX(CASE WHEN rn = 2 THEN ddd       ELSE NULL END)  AS ddd_2,
    MAX(CASE WHEN rn = 2 THEN phone     ELSE NULL END)  AS phone_2,
    MAX(CASE WHEN rn = 2 THEN [type]    ELSE NULL END)  AS type_2,
    MAX(CASE WHEN rn = 2 THEN origin    ELSE NULL END)  AS origin_2,

    -- Third set of columns.
    MAX(CASE WHEN rn = 3 THEN ddd       ELSE NULL END)  AS ddd_3,
    MAX(CASE WHEN rn = 3 THEN phone     ELSE NULL END)  AS phone_3,
    MAX(CASE WHEN rn = 3 THEN [type]    ELSE NULL END)  AS type_3,
    MAX(CASE WHEN rn = 3 THEN origin    ELSE NULL END)  AS origin_3,

    -- Fourth set of columns
    MAX(CASE WHEN rn = 4 THEN ddd       ELSE NULL END)  AS ddd_4,
    MAX(CASE WHEN rn = 4 THEN phone     ELSE NULL END)  AS phone_4,
    MAX(CASE WHEN rn = 4 THEN [type]    ELSE NULL END)  AS type_4,
    MAX(CASE WHEN rn = 4 THEN origin    ELSE NULL END)  AS origin_4
FROM
    Sequenced
GROUP BY 
    cpf
;

结果

这有很多内容需要考虑。让我们分解一下这些步骤。首先 CTE 添加一个分区行号:

rn  cpf     ddd phone       type    origin
1   11111111111 83  81021111    M   SERASA
2   11111111111 83  87472222    M   SERASA
3   11111111111 83  81023333    M   TRANSUNION
4   11111111111 83  88724444    M   TRANSUNION
5   11111111111 83  87475555    M   TRANSUNION
1   22222222222 43  36271111    F   SERASA
2   22222222222 44  36272222    F   SERASA
3   22222222222 43  36273333    F   TRANSUNION
4   22222222222 43  36284444    F   TRANSUNION
1   33333333333 51  51811111    F   SERASA
2   33333333333 51  56212222    F   SERASA
3   33333333333 51  96213333    M   SERASA

接下来,case 表达式根据行号将行移动到列中。我只包含了一些列和行以使其更易于阅读。

rn  cpf         phone_1    phone_2    phone_3
1   11111111111 81021111   NULL       NULL
2   11111111111 NULL       87472222   NULL
3   11111111111 NULL       NULL       87475555

最后,分组删除了多余的行。 max 函数有利于内容而不是空值。我再次删除了行和列,以便更容易理解。

rn  cpf         phone_1    phone_2    phone_3
1   11111111111 81021111   87472222   87475555
2   22222222222 36271111   36272222   36273333
...

【讨论】:

  • 这只是我在这里的第一个问题,我会尽力让大家更容易提供帮助(也许用我自己的问题代码和生成示例数据的查询)。您的解决方案运行良好,我担心它会运行的时间,但只花了 10 分钟。谢谢!
  • 您的表中有多少条记录?我只问是因为 10 分钟听起来很慢。或许可以帮助您加快速度。
  • 原始表中有 3300 万条记录,结果表中有 750 万条记录(我在 select 之前使用了 Insert Into 来填充它)。由于该过程将每月运行一次,因此 10 分钟是可以的,但我理解您的意思,如果您对如何加快速度有任何建议,我将非常感谢您的帮助。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 1970-01-01
相关资源
最近更新 更多