使用CTE将父列表列为列

时间:2016-04-05 18:44:46

标签: sql-server sql-server-2008 tsql sql-server-2008-r2

我有两个表,ticketproblem。每张票都有问题,每个问题可能有也可能没有父问题。样本数据:

create table #problem (problem_type_id int, problem_type_name varchar(32), parent_id int)
insert #problem
select 1,  'Change Request', NULL union
select 5,  'Level 1', 1 union
select 10, 'Software', 5 union
select 15, 'Applications', 10 union
select 20, 'Update', 15 union
select 6,  'Level 2', 1 union
select 11, 'Hardware', 6 union
select 16, 'Install', 11

create table #ticket (ticket_id int, problem_type_id int)
insert #ticket 
select 1, 20 union
select 2, 16

这是我需要的结果:

+-----------+-----------------+-----------------+-----------------+-----------------+-----------------+
| ticket_id | problem_level_1 | problem_level_2 | problem_level_3 | problem_level_4 | problem_level_5 |
+-----------+-----------------+-----------------+-----------------+-----------------+-----------------+
| 1         | Change Request  | Level 1         | Software        | Applications    | Update          |
| 2         | Change Request  | Level 2         | Hardware        | Install         |                 |
+-----------+-----------------+-----------------+-----------------+-----------------+-----------------+

假设顶层父级最多有四个后代,我如何以顶级父级的相反顺序解决问题?这就是我到目前为止所拥有的:

;with probs (parent_id, problem_type_id, problem_type_name, level)
as
(
    -- anchor member definition
    select parent_id, problem_type_id, problem_type_name, 0 as level
    from #problem
    where parent_id is null
    union all
    -- recursive member definition
    select a.parent_id, a.problem_type_id, a.problem_type_name, level + 1
    from #problem a
    join probs as b on b.problem_type_id = a.parent_id
)
select t.ticket_id,
p1.level, /* p1.problem_type_id, */ p1.problem_type_name,
p2.level, /* p2.problem_type_id, */ p2.problem_type_name,
p3.level, /* p3.problem_type_id, */ p3.problem_type_name,
p4.level, /* p4.problem_type_id, */ p4.problem_type_name,
p5.level, /* p5.problem_type_id, */ p5.problem_type_name
from #ticket t
join probs p1 on p1.problem_type_id = t.problem_type_id
left join probs p2 on p2.problem_type_id = p1.parent_id and p2.level = p1.level - 1
left join probs p3 on p3.problem_type_id = p2.parent_id and p3.level = p2.level - 1
left join probs p4 on p4.problem_type_id = p3.parent_id and p4.level = p3.level - 1
left join probs p5 on p5.problem_type_id = p4.parent_id and p5.level = p4.level - 1
order by t.ticket_id

我似乎无法弄清楚如何首先获得0级列,然后是孩子们。

1 个答案:

答案 0 :(得分:0)

你快到了。您可以使用PIVOT来实现此目的,如下所示:

示例数据:

CREATE TABLE #problem(problem_type_id   INT
                , problem_type_name VARCHAR(32)
                , parent_id         INT);

INSERT INTO #problem
      SELECT 1
          , 'Change Request'
          , NULL
      UNION
      SELECT 5
          , 'Level 1'
          , 1
      UNION
      SELECT 10
          , 'Software'
          , 5
      UNION
      SELECT 15
          , 'Applications'
          , 10
      UNION
      SELECT 20
          , 'Update'
          , 15
      UNION
      SELECT 6
          , 'Level 2'
          , 1
      UNION
      SELECT 11
          , 'Hardware'
          , 6
      UNION
      SELECT 16
          , 'Install'
          , 11;

CREATE TABLE #ticket(ticket_id       INT
                , problem_type_id INT);

INSERT INTO #ticket
      SELECT 1
          , 20
      UNION
      SELECT 2
          , 16;

QUERY:

;WITH probs(parent_id
        , problem_type_id
        , problem_type_name
        , level)
    AS (
    -- anchor member definition
    SELECT parent_id
        , problem_type_id
        , problem_type_name
        , 0 AS level
    FROM     #problem
    WHERE   parent_id IS NULL
    UNION ALL
    -- recursive member definition
    SELECT a.parent_id
        , a.problem_type_id
        , a.problem_type_name
        , level + 1
    FROM   #problem a
          JOIN probs AS b ON b.problem_type_id = a.parent_id)
    SELECT t.ticket_id
        , p1.level 

          /* p1.problem_type_id, */

        , p1.problem_type_name
    INTO #preResult
    FROM     #ticket t
            JOIN probs p1 ON p1.problem_type_id = t.problem_type_id
            LEFT JOIN probs p2 ON p2.problem_type_id = p1.parent_id
                             AND p2.level = p1.level - 1
            LEFT JOIN probs p3 ON p3.problem_type_id = p2.parent_id
                             AND p3.level = p2.level - 1
            LEFT JOIN probs p4 ON p4.problem_type_id = p3.parent_id
                             AND p4.level = p3.level - 1
            LEFT JOIN probs p5 ON p5.problem_type_id = p4.parent_id
                             AND p5.level = p4.level - 1
    UNION
    SELECT t.ticket_id
        , p2.level 

          /* p2.problem_type_id, */

        , p2.problem_type_name
    FROM     #ticket t
            JOIN probs p1 ON p1.problem_type_id = t.problem_type_id
            LEFT JOIN probs p2 ON p2.problem_type_id = p1.parent_id
                             AND p2.level = p1.level - 1
            LEFT JOIN probs p3 ON p3.problem_type_id = p2.parent_id
                             AND p3.level = p2.level - 1
            LEFT JOIN probs p4 ON p4.problem_type_id = p3.parent_id
                             AND p4.level = p3.level - 1
            LEFT JOIN probs p5 ON p5.problem_type_id = p4.parent_id
                             AND p5.level = p4.level - 1
    UNION
    SELECT t.ticket_id
        , p3.level 

          /* p3.problem_type_id, */

        , p3.problem_type_name
    FROM     #ticket t
            JOIN probs p1 ON p1.problem_type_id = t.problem_type_id
            LEFT JOIN probs p2 ON p2.problem_type_id = p1.parent_id
                             AND p2.level = p1.level - 1
            LEFT JOIN probs p3 ON p3.problem_type_id = p2.parent_id
                             AND p3.level = p2.level - 1
            LEFT JOIN probs p4 ON p4.problem_type_id = p3.parent_id
                             AND p4.level = p3.level - 1
            LEFT JOIN probs p5 ON p5.problem_type_id = p4.parent_id
                             AND p5.level = p4.level - 1
    UNION
    SELECT t.ticket_id
        , p4.level 

          /* p4.problem_type_id, */

        , p4.problem_type_name
    FROM     #ticket t
            JOIN probs p1 ON p1.problem_type_id = t.problem_type_id
            LEFT JOIN probs p2 ON p2.problem_type_id = p1.parent_id
                             AND p2.level = p1.level - 1
            LEFT JOIN probs p3 ON p3.problem_type_id = p2.parent_id
                             AND p3.level = p2.level - 1
            LEFT JOIN probs p4 ON p4.problem_type_id = p3.parent_id
                             AND p4.level = p3.level - 1
            LEFT JOIN probs p5 ON p5.problem_type_id = p4.parent_id
                             AND p5.level = p4.level - 1
    UNION
    SELECT t.ticket_id
        , p5.level 

          /* p5.problem_type_id, */

        , p5.problem_type_name
    FROM   #ticket t
          JOIN probs p1 ON p1.problem_type_id = t.problem_type_id
          LEFT JOIN probs p2 ON p2.problem_type_id = p1.parent_id
                            AND p2.level = p1.level - 1
          LEFT JOIN probs p3 ON p3.problem_type_id = p2.parent_id
                            AND p3.level = p2.level - 1
          LEFT JOIN probs p4 ON p4.problem_type_id = p3.parent_id
                            AND p4.level = p3.level - 1
          LEFT JOIN probs p5 ON p5.problem_type_id = p4.parent_id
                            AND p5.level = p4.level - 1;

SELECT ticket_id,  problem_level_1=[0] 
             ,  problem_level_2=[1]
             ,  problem_level_3=[2]
             ,  problem_level_4=[3]
             ,  problem_level_5=[4]
FROM
      (SELECT ticket_id
           , level
           , problem_type_name
       FROM   #preResult) AS p PIVOT(MAX(problem_type_name) FOR [level] IN([0]
                                                            , [1]
                                                            , [2]
                                                            , [3]
                                                            , [4])) AS unpvt;

结果:

enter image description here