连续

2012-08-03 43 views
5

表查找最后不为空值的列名是这个样子连续

 
ID A1 A2 A3 A4 A5 A6 A7 A8 A9 
1 YE YE YE NULL YE YE YE NULL NULL 
2 YE YE YE NULL NULL NULL NULL NULL NULL 
3 YE YE YE YE YE YE YE YE NULL 

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

 
ID LAST 
1 A7 
2 A3 
3 A8 

任何帮助?

+0

我可以说“模式看起来很糟糕”吗? :-)可以使用反向优先条件来完成。 – 2012-08-03 18:44:14

回答

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子句计算布尔表达式。

另外,或许UNPIVOT(或ROLLUP [?]或手动UNION)可以被使用。也就是说,转动固定集列名的入值,那么它是一个简单的查询。即,如果表是标准化的,这可以很容易地:-)

select 
    id, 
    max(colName) as lastNonNullColumn 
from <<normalized_derived_table>> 
where colValue is not null 
group by id 
+1

您的CASE表达式具有最佳的执行计划并且占用最少的CPU。 UNPIVOT行动需要昂贵的SORT – ErikE 2012-08-03 19:51:44

+0

其工作正常。谢谢你们。 – user1574813 2012-08-04 09:18:38

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 with Demo

编辑,其实它并不需要那么复杂:

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 with Demo

+0

你的两个版本都可以工作,但需要列名可以排序(包括没有A10或更高版本)。我很确定OP的列没有被命名为A1 - A9。 – ErikE 2012-08-03 19:40:37

+0

@ErikE是真的,但根据发布的要求,这将起作用。如果他们扩大了他们的需求,那将是有帮助的。 OP在表格中列出了a1-a9栏。 – Taryn 2012-08-03 19:46:00

+0

够公平的!给出的问题是一个很好的答案。 – ErikE 2012-08-03 21:04:19

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的最新值会变得更简单一些!