連續

2012-08-03 41 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的最新值會變得更簡單一些!