2016-04-25 72 views
5

如何告訴LAG函數獲取最後一個「非空值」?滯後函數和NULLS

例如,請參閱我的表下面,其中列B和C上有幾個NULL值。 我想用最後一個非空值填充空值。我試圖做的是通過LAG功能,像這樣:

case when B is null then lag (B) over (order by idx) else B end as B, 

,但是當我有連續兩個或兩個以上的空值(見C列第3行的NULL值完全不是那麼回事 - 我我想它是原來的0.50)。

任何想法我怎麼能做到這一點? (它沒有被使用LAG功能,任何其他的想法,歡迎)

一些假設:

  • 行數是動態的;
  • 第一個值將始終爲非空;
  • 一旦我有一個NULL,所有到NULL結束 - 所以我想填充它的最新值。

感謝

enter image description here

+0

伊茨克奔甘寫道:一個關於這個問題的博客:http://sqlmag.com/sql-server/how-previous-and-next-condition。 Unfortunatley SQL Server不支持'LAST_VALUE'中的'IGNORE NULLS'選項,那很簡單:'LAST_VALUE(B IGNORE NULLS)OVER(ORDER BY IDX)'。 – dnoeth

回答

1

如果是空一路到底,然後可以走捷徑

declare @b varchar(20) = (select top 1 b from table where b is not null order by id desc); 
declare @c varchar(20) = (select top 1 c from table where c is not null order by id desc); 
select is, isnull(b,@b) as b, insull(c,@c) as c 
from table; 
+0

good aproach,我不想聲明變量,所以我最終做了這樣的事情: case B when null(then select top 1B from where B不是空訂單idx desc)否則B結束爲B 偉大的想法,非常感謝 – Diego

+0

我認爲變量更清晰的閱讀,它可以確保查詢優化器只做一次。 – Paparazzi

4

你可以做一個改變你ORDER BY,迫使空值是第一個在你的排序,但可能是昂貴的...

lag(B) over (order by CASE WHEN B IS NULL THEN -1 ELSE idx END) 

或者,使用子查詢一次計算替換值。可能更便宜的大套,但非常笨重。
- 憑藉在最後來的所有的NULL
- 該LAG不依賴於

COALESCE(
    B, 
    (
     SELECT 
      sorted_not_null.B 
     FROM 
     (
      SELECT 
       table.B, 
       ROW_NUMBER() OVER (ORDER BY table.idx DESC) AS row_id 
      FROM 
       table 
      WHERE 
       table.B IS NOT NULL 
     ) 
      sorted_not_null 
     WHERE 
      sorted_not_null.row_id = 1 
    ) 
) 

(這應該是更大的數據集速度更快,比LAG或使用OUTER APPLY與相關子-queries,僅僅是因爲價值被計算一次,整潔,你可以計算和存儲[last_known_value]爲變量每一列,那麼就使用COALESCE(A, @last_known_A), COALESCE(B, @last_known_B), etc

+0

+1這似乎工作,但我的「表」實際上是一個大的查詢,我不想運行多次,你的解決方案將需要。非常感謝幫助 – Diego

+0

@Diego - 除了使用LAG之外,這裏的所有其他答案(以及我能想到的每種方法)都會遇到這個問題。 – MatBailie

6

你可以用outer apply運營商做到這一點:

select t.id, 
     t1.colA, 
     t2.colB, 
     t3.colC 
from table t 
outer apply(select top 1 colA from table where id <= t.id and colA is not null order by id desc) t1 
outer apply(select top 1 colB from table where id <= t.id and colB is not null order by id desc) t2 
outer apply(select top 1 colC from table where id <= t.id and colC is not null order by id desc) t3; 

無論有多少個空值或空「島」,這都可以工作。你可能有值,然後是空值,然後是值,再次是空值。它仍然會工作。


但如果假設(你的問題)認爲:

一旦我有一個NULL,是NULL所有到最後 - 所以我想用最新的值來填充它。

有一個更有效的解決方案。我們只需要找到最新的(當按idx排序時)值。修改上面的查詢,從子查詢去除where id <= t.id

select t.id, 
     colA = coalesce(t.colA, t1.colA), 
     colB = coalesce(t.colB, t2.colB), 
     colC = coalesce(t.colC, t3.colC) 
from table t 
outer apply (select top 1 colA from table 
      where colA is not null order by id desc) t1 
outer apply (select top 1 colB from table 
      where colB is not null order by id desc) t2 
outer apply (select top 1 colC from table 
      where colC is not null order by id desc) t3; 
+0

嘿,謝謝,但正如我所說,「行數是動態的」,那麼如何與5行工作? – Diego

+2

它會工作得很完美。你關心什麼? –

+0

@diego - 無論行數是多少,它都可以工作......但是,隨着數據集的增長,它將有潛在的指數成本*(第1000行的每個子查詢的成本高於第999行的成本)*,但對於小型數據集來說,它確實很整潔。 – MatBailie

-3
UPDATE table 
SET B = (@n := COALESCE(B , @n)) 
WHERE B is null; 
+0

這個問題是關於'SQL Server'。 –

+0

正確的想法,但這是MySQL的符號。它*可以*在SQL服務器中完成,但不是那樣寫的。 – MatBailie

+0

更新表 SET B =(從表中選擇last_value(B忽略空值)(按idx排序)b)其中B爲空; – Adesh