2013-05-15 70 views
3

我有一個SQL表類似於以下內容:SQL填充根據以往的行

ID, Date, D1, D2, D3 
1 1/1/13 0 X A 
2 1/2/13   
3 1/3/13 1 
4 1/4/13   B 
5 1/5/13 

,我需要更新以下內容:

ID, Date, D1, D2, D3 
1 1/1/13 0 X A 
2 1/2/13 0 X A  
3 1/3/13 1 X A 
4 1/4/13 1 X B 
5 1/5/13 1 X B 

基本上填補所有具有之前值的空值。這隻會執行一次。循環是最好的選擇嗎?或者有什麼會更有效?

+1

你使用的是什麼rdbms? –

+1

您是否將前一個定義爲「帶有下一個最低的」ID「值」?或者「下一個最低的日期值」?或者你有其他的定義你想使用?表本質上是無序的。如果你想做一些涉及秩序的事情,你必須給我們一個明確的規則來定義這個秩序。 –

回答

5

假設您正在使用SQL-Server,您可以使用此公用表表達式:

WITH cte 
    AS (SELECT T1.id, 
       T1.date, 
       d1 = COALESCE(T1.d1, (SELECT TOP 1 d1 
             FROM dbo.tablename T2 
             WHERE T2.id < T1.id 
              AND d1 IS NOT NULL 
             ORDER BY id DESC)), 
       d2 = COALESCE(T1.d2, (SELECT TOP 1 d2 
             FROM dbo.tablename T2 
             WHERE T2.id < T1.id 
              AND d2 IS NOT NULL 
             ORDER BY id DESC)), 
       d3 = COALESCE(T1.d3, (SELECT TOP 1 d3 
             FROM dbo.tablename T2 
             WHERE T2.id < T1.id 
              AND d3 IS NOT NULL 
             ORDER BY id DESC)) 
     FROM dbo.tablename T1) 
UPDATE T 
SET T.d1 = C.d1, 
     T.d2 = C.d2, 
     T.d3 = C.d3 
FROM dbo.tablename T 
     INNER JOIN cte C 
       ON T.id = C.id 
WHERE T.d1 IS NULL 
     OR T.d2 IS NULL 
     OR T.d3 IS NULL 

DEMO

編輯既然你在,你有'',而不是評論提到null,這是一個修改後的版本,支持兩種:

WITH cte AS 
     (SELECT T1.id, 
       T1.date, 
       d1 = CASE WHEN COALESCE(T1.d1, '') <> '' THEN d1 
         ELSE(SELECT TOP 1 d1 
          FROM dbo.tablename T2 
          WHERE T2.id < T1.id 
            AND COALESCE(T2.d1, '') <> '' 
          ORDER BY T2.id DESC) END, 
       d2 = CASE WHEN COALESCE(T1.d2, '') <> '' THEN d2 
         ELSE(SELECT TOP 1 d2 
          FROM dbo.tablename T2 
          WHERE T2.id < T1.id 
            AND COALESCE(T2.d2, '') <> '' 
          ORDER BY T2.id DESC) END, 
       d3 = CASE WHEN COALESCE(T1.d3, '') <> '' THEN d3 
         ELSE(SELECT TOP 1 d3 
          FROM dbo.tablename T2 
          WHERE T2.id < T1.id 
            AND COALESCE(T2.d3, '') <> '' 
          ORDER BY T2.id DESC) END 
     FROM dbo.tablename T1) 
UPDATE T 
SET T.d1 = C.d1, 
     T.d2 = C.d2, 
     T.d3 = C.d3 
FROM dbo.tablename T 
     INNER JOIN cte C 
       ON T.id = C.id 
WHERE COALESCE(T.d1, '') = '' 
     OR COALESCE(T.d2, '') = '' 
     OR COALESCE(T.d3, '') = '' 

DEMO

+0

出於好奇,爲什麼'T2.id <= T1.id - 1'而不是'T2.id

+0

@Damien_The_Unbeliever:是的,這是我的第一個版本中沒有使用'<='但是'='並且沒有'WHERE' +'ORDER BY'的剩餘部分。 (已編輯) –

+0

工程,除了我的nvarchar列實際上是''而不是空。有沒有什麼辦法可以修改它來爲此工作?在此之前,我可以運行一個更新腳本,將其更改爲我想的實際NULLS。 – Shawn

1

假設我們使用ID列確定順序,您可以不使用循環。

無論您做,這是另一回事 - 它並不漂亮:

declare @t table (ID int, Date date, D1 int, D2 char(1), D3 char(1)) 
insert into @t(ID, Date, D1, D2, D3) values 
(1,'20130101',0,'X','A'), 
(2,'20130201',null,null,null),  
(3,'20130301',1,null,null), 
(4,'20130401',null,null,'B'), 
(5,'20130501',null,null,null) 

update a 
set 
    a.D1 = COALESCE(a.D1,d1.D1), 
    a.D2 = COALESCE(a.D2,d2.D2), 
    a.D3 = COALESCE(a.D3,d3.D3) 
from 
    @t a 
     left join 
    @t D1 
     on 
      D1.ID < a.ID and 
      D1.D1 IS NOT NULL 
     left join 
    @t D1_anti 
     on 
      D1_anti.ID < a.ID and 
      D1_anti.D1 is not null and 
      D1_anti.ID > D1.ID 
     left join 
    @t D2 
     on 
      D2.ID < a.ID and 
      D2.D2 IS NOT NULL 
     left join 
    @t D2_anti 
     on 
      D2_anti.ID < a.ID and 
      D2_anti.D2 is not null and 
      D2_anti.ID > D2.ID 
     left join 
    @t D3 
     on 
      D3.ID < a.ID and 
      D3.D3 IS NOT NULL 
     left join 
    @t D3_anti 
     on 
      D3_anti.ID < a.ID and 
      D3_anti.D3 is not null and 
      D3_anti.ID > D3.ID 
where 
    D1_anti.ID is null and 
    D2_anti.ID is null and 
    D3_anti.ID is null 

select * from @t 

基本上,我們執行連接,試圖找到可以適用早先的行,然後執行_anti連接以確保每個找到的前一行是存在的最新這樣的行。