5

表是这样的

编号 A1 A2 A3 A4 A5 A6 A7 A8 A9
1 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是 是
2 是 是 是 空 空 空 空 空 空
3 耶耶耶耶耶耶耶耶耶耶 NULL

其中 ID 是主键。
我想连续获取最后一个非空值的列名,结果是这样的

编号最后
1 A7
2 A3
3 A8

对此有什么帮助吗?

4

3 回答 3

2

尽管我对此模式存有疑虑,但请考虑这个“反向优先”条件:

select
  id,
  case
    -- first match terminates search
    when A9 is not null then 'A9'
    when A8 is not null then 'A8'
    when A7 is not null then 'A7'
    ..
    else null
  as lastNonNullColumn
from ..

TSQL 中保证了评估的顺序(参见CASE),所以我们只是向后蠕动 :)

按指定顺序计算每个 WHEN 子句的 Boolean_expression。

此外,也许可以使用UNPIVOT(或ROLLUP[?] 或 manual UNION)。也就是说,将一组固定的列名转换为值,然后这是一个简单的查询..也就是说,如果表被规范化,这可以很容易地完成:-)

select
  id,
  max(colName) as lastNonNullColumn
from <<normalized_derived_table>>
where colValue is not null
group by id
于 2012-08-03T18:50:19.530 回答
2

这个怎么样?它使用一个UNPIVOT来转换数据,然后您将选择不为空/空白的最大最后一个值。

;with cte as
(
  select id
    , last
    , value
    , row_number() over(partition by id order by last) rn
  from
  (
      select id, 
          isnull(a1, '') as a1, 
          isnull(a2, '') as a2, 
          isnull(a3, '') as a3, 
          isnull(a4, '') as a4, 
          isnull(a5, '') as a5, 
          isnull(a6, '') as a6, 
          isnull(a7, '') as a7, 
          isnull(a8, '') as a8, 
          isnull(a9, '') as a9
      from t
  ) x
  unpivot
  (
      value
      for last in (a1, a2, a3, a4, a5, a6, a7, a8, a9)
  ) u
) 
select id, max(last) as last
from cte
where value != ''
group by id

请参阅带有演示的 SQL Fiddle

编辑,实际上它不需要那么复杂:

select id
  , max(last) last
from
(
    select id, a1, a2, a3, a4, a5, a6, a7, a8, a9
    from t
) x
unpivot
(
    value
    for last in (a1, a2, a3, a4, a5, a6, a7, a8, a9)
) u
group by id

请参阅带有演示的 SQL Fiddle

于 2012-08-03T19:10:01.073 回答
1

这是一个伪 UNPIVOT 版本,可让您指定列的顺序(如果列名不按位置排序)。

SELECT
   T.ID,
   X.Name
FROM
   T
   CROSS APPLY (
      SELECT TOP 1 Name FROM (
         VALUES (1, 'A1', T.A1), (2, 'A2', T.A2), (3, 'A3', T.A3), (4, 'A4', T.A4),
         (5, 'A5', T.A5), (6, 'A6', T.A6), (7, 'A7', T.A7), (8, 'A8', T.A8),
         (9, 'A9', T.A9)
      ) X (Pos, Name, Col)
      WHERE Col IS NOT NULL
      ORDER BY X.Pos DESC
   ) X;

然而,虽然实际 IO 和 CPU 并没有比自然 UNPIVOT 方法差多少(执行计划看起来很糟糕,但实际服务器影响并没有差多少),但这并不是表现最好的。@pst 给出的简单 CASE 表达式是。

假设列名可以按原样排序,则 UNPIVOT 可以进一步简化:

SELECT ID, Max(Last)
FROM T UNPIVOT (Value FOR Last IN (A1, A2, A3, A4, A5, A6, A7, A8, A9)) U
GROUP BY ID;

最后,这是我想到的一个疯狂版本,不幸的是它的性能比其他版本差:

SELECT
   T.ID,
   Coalesce(
      (SELECT 'A9' WHERE T.A9 IS NOT NULL),
      (SELECT 'A8' WHERE T.A8 IS NOT NULL),
      (SELECT 'A7' WHERE T.A7 IS NOT NULL),
      (SELECT 'A6' WHERE T.A6 IS NOT NULL),
      (SELECT 'A5' WHERE T.A5 IS NOT NULL),
      (SELECT 'A4' WHERE T.A4 IS NOT NULL),
      (SELECT 'A3' WHERE T.A3 IS NOT NULL),
      (SELECT 'A2' WHERE T.A2 IS NOT NULL),
      (SELECT 'A1' WHERE T.A1 IS NOT NULL)
   ) LastNotNullColumn
FROM T
ORDER BY ID

从理论上讲,引擎可以提出一个看起来更像 CASE 表达式版本的计划,但事实并非如此。该计划看起来非常疯狂,每个 select 语句有一个表对象,并且占用大约两倍于 CASE 表达式的 CPU。

我测试的所有版本都使用相同数量的逻辑读取,只是 CPU 不同。我使用了 15,000 行来测试。

最后,我不能凭良心不警告您您的架构可能不是最好的。虽然我不知道你的数据是什么,但你试图找到最后一个可能表明这些列代表了某个生命周期的时间或阶段——这不是正确的数据库设计。相反,存储未透视的数据。当您需要一个旋转的结果集时,您可以 PIVOT。而且,查询每个 ID 的最新值变得更简单了!

于 2012-08-03T19:55:02.440 回答