行到列(Pivot?)

时间:2016-02-11 13:02:47

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

我有一个具有以下结构的表:

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,但我没有找到如何使用它,因为我没有定义列的字段,也没有& #39; t有聚合(总和,计数等)。

我使用表格本身的连接创建了一些数据(100行),但由于数据量(该表有3300万行),此解决方案无法正常工作。

现在我被卡住了,任何帮助都会受到赞赏。

1 个答案:

答案 0 :(得分: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,为第二条记录分配了1,依此类推。为了避免无休止地重新输入ROW_NUMBER(),我将结果转换为common table expression。 CTE是创建临时,可重用数据集的好方法。最后一步是将编号为1的记录移动到第一组列中,将编号为2的记录移动到第二组中,等等。

将行移动到列中可能需要更详细的解释。我已根据ROW_NUMBER()返回的序列号使用CASE expressions条件移动值。然后我通过cpf GROUPED得到结果。用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函数优先于null而不是内容。我再次删除了行和列,以便更容易理解。

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