【问题标题】:Merging Multiple Rows into a single Row (SQL)将多行合并为单行 (SQL)
【发布时间】:2016-04-05 17:06:48
【问题描述】:

我正在开展一个为不同订单创建 SLA 的项目。为此,我需要将多行合并为一行,尽管有时它会超过 1 行。以下是当前数据的设置方式:

OrderNumber | Stage1 | Stage2 | Stage3 | Stage4 |
=================================================
1           |1/1/2016|*NULL*  |*NULL*  | *NULL*
1           |*NULL*  |2/1/2016|*NULL*  | *NULL*
1           |*NULL*  |*NULL*  |3/1/2016| *NULL*
1           |*NULL*  |*NULL*  |*NULL*  | 4/1/2016
1           |*NULL*  |5/1/2016|*NULL*  | *NULL*

我希望看到数据的方式是这样的:

OrderNumber | Stage1 | Stage2 | Stage3 | Stage4 |
=================================================
1           |1/1/2016|2/1/2016|3/1/2016| 4/1/2016
1           |1/1/2016|5/1/2016|*NULL*  | *NULL*

OrderNumber | Stage1 | Stage2 | Stage3 | Stage4 |
=================================================
1           |1/1/2016|2/1/2016|3/1/2016| 4/1/2016
1           |*NULL*  |5/1/2016|*NULL*  | *NULL*

我看到了两个问题,1)将多行合并为一行,2)如果一个值在列中出现多次,则在新行中显示它(理想情况下,前阶段的值出现在前面列,但这不是必需的)。

我找到的最接近的解决方案是:Merge two rows in SQL,但它不适用于这个问题。

任何帮助将不胜感激。

编辑更新标签

【问题讨论】:

  • 如果第一阶段有两个非空日期而不是一个会发生什么?哪一个与 stage2 两个日期配对?
  • 我能想到的最简单的方法是在值不为空时为每个阶段分配一个行号。然后根据rownumber加入数据
  • 是sql-server还是mysql? sql-server 表示 Microsoft SQL Server。请正确使用标签
  • "如果第 1 阶段有两个非空日期而不是一个,会发生什么情况?您将哪一个与第 2 阶段的两个日期配对?" – Juan Carlos Oropeza 在这种情况下,第一个日期(最早的日期)将在第一行,第二个日期(较晚的日期)将在两个阶段的第二行。 “将多行合并为单行的可能重复项” – Ken White 我曾研究过这一行,看起来重点是数据的串联,这在此处不起作用。

标签: sql-server merge


【解决方案1】:

我使用 SQL Server 2012 解决了这个问题,使用 window functions。由于 MySQL 也支持窗口函数,所以不管是哪个数据库。如果 MySQL 不支持 CTE,可以用派生表替换。

有 4 个阶段,所以我们需要 4 个连接。

;with cte as (
select t1.OrderNumber, t1.Stage1, t2.Stage2, t3.Stage3, t4.Stage4
, LAG(t1.Stage1) over (partition by t1.OrderNumber order by t1.Stage1) AS Prev1
, LAG(t2.Stage2) over (partition by t2.OrderNumber order by t2.Stage2) AS Prev2
, LAG(t3.Stage3) over (partition by t3.OrderNumber order by t3.Stage3) AS Prev3
, LAG(t4.Stage4) over (partition by t4.OrderNumber order by t4.Stage4) AS Prev4
from #t t1
  inner join #t t2 on t2.OrderNumber = t1.OrderNumber and t2.Stage2 is not null
  inner join #t t3 on t3.OrderNumber = t1.OrderNumber and t3.Stage3 is not null
  inner join #t t4 on t4.OrderNumber = t1.OrderNumber and t4.Stage4 is not null
where t1.Stage1 is not null
)
select OrderNumber
, IIF(Stage1 <> Prev1 or Prev1 is null, Stage1, Null) AS Stage1 
, IIF(Stage2 <> Prev2 or Prev2 is null, Stage2, Null) AS Stage2
, IIF(Stage3 <> Prev3 or Prev3 is null, Stage3, Null) AS Stage3
, IIF(Stage4 <> Prev4 or Prev4 is null, Stage4, Null) AS Stage4
from cte

用你的表名替换#t

结果:

+-------------+------------+------------+------------+------------+
| OrderNumber |   Stage1   |   Stage2   |   Stage3   |   Stage4   |
+-------------+------------+------------+------------+------------+
|           1 | 2016-01-01 | 2016-01-02 | 2016-01-03 | 2016-01-04 |
|           1 | NULL       | 2016-01-05 | NULL       | NULL       |
+-------------+------------+------------+------------+------------+

【讨论】:

  • 这看起来可以完美运行。谢谢闪烁!
【解决方案2】:

您可以取消透视数据,然后再次透视

SELECT OrderNumber, [Stage1], [Stage2], [Stage3], [Stage4] 
FROM (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY Vals ORDER BY Val) Rn FROM 
    Table1 t
    UNPIVOT
    (
        Val
        FOR Vals IN ([Stage1], [Stage2], [Stage3], [Stage4])
    ) up
) t
PIVOT 
(
    MAX(Val)
    FOR Vals IN ([Stage1], [Stage2], [Stage3], [Stage4])
) p

【讨论】:

  • @Fowipple 我真的希望你在使用你在网上找到的任何旧 sql 之前检查执行计划
  • 是的,我不知道我在想什么@JamieD77。
  • @shawnt00 大声笑.. 我试图让你的工作正常,所以我可以看看它如何比较性能,但我无法弄清楚。我认为它至少会比选定的答案更好。
  • 我已经按照我最初的尝试发布了一个经过测试的查询。我想你可以对它进行测试,但我敢打赌你会赢。
【解决方案3】:
with data as (
    select
        OrderNumber, Stage1, Stage2, Stage3, Stage4,
        row_number() over (
            partition by OrderNumber
            order by
                case when Stage1 is not null then 0 else 1 end, Stage1,
                case when Stage2 is not null then 0 else 1 end, Stage2,
                case when Stage3 is not null then 0 else 1 end, Stage3,
                case when Stage4 is not null then 0 else 1 end, Stage4) rn,
        row_number() over (
            partition by OrderNumber
            order by case when Stage1 is not null then 0 else 1 end, Stage1) r1,
        row_number() over (
            partition by OrderNumber
            order by case when Stage2 is not null then 0 else 1 end, Stage2) r2,
        row_number() over (
            partition by OrderNumber
            order by case when Stage3 is not null then 0 else 1 end, Stage3) r3,
        row_number() over (
            partition by OrderNumber
            order by case when Stage4 is not null then 0 else 1 end, Stage4) r4
    from T
)
select
    OrderNumber, s1.Stage1, s2.Stage2, s3.Stage3, s4.Stage4
from
    data d
    cross apply (select d1.Stage1 from data d1 where d1.r1 = d.rn) s1(Stage1)
    cross apply (select d2.Stage2 from data d2 where d2.r2 = d.rn) s2(Stage2)
    cross apply (select d3.Stage3 from data d3 where d3.r3 = d.rn) s3(Stage3)
    cross apply (select d4.Stage4 from data d4 where d4.r4 = d.rn) s4(Stage4)
where
    coalesce(s1.Stage1, s2.Stage2, s3.Stage3, s4.Stage4) is not null

【讨论】:

  • 这个 rdbms 是干什么用的?
  • @JamieD77,OP 说 SQL Server。
  • 你试过在 sql server 上运行这个吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 2018-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多