2012-08-02 76 views
4

@@版本1更新上一行中非空值的空列值

使用SQL Server 2008時,我試圖將值級聯到列的下方。我有一個包含組ID(GID)和Seq的表格,其中包含組內記錄的排序。對於存在的列,在這種情況下,名稱和薪酬 - 我真正的表有超過50列,如果他們包含空值我需要更新NULL值與該列的前一行中包含非空值的值。

這裏是值得說明這一點:

GID Seq Name Salary 
1 1 James NULL 
1 2 NULL 100 
1 3 NULL NULL 
2 1 NULL 81 
2 2 Smith NULL 
2 3 NULL NULL 
3 1 Charles NULL 
3 2 NULL NULL 
3 3 Brown NULL 
3 4 NULL 75 
4 0 Ron 50 
4 1 NULL 20 
4 2 NULL NULL 

我的結果應該是:

GID Seq Name Salary 
1 1 James NULL 
1 2 James 100 
1 3 James 100 
2 1 NULL 81 
2 2 Smith 81 
2 3 Smith 81 
3 1 Charles NULL 
3 2 Charles NULL 
3 3 Brown NULL 
3 4 Brown 75 
4 0 Ron 50 
4 1 Ron 20 
4 2 Ron 20 

我要做到這一點,而無需使用動態SQL,環或遊標。

代碼簡單的測試案例:

DECLARE @Test TABLE (GID int, Seq int, Name varchar(50), Salary decimal) 

INSERT INTO @Test VALUES (1, 1, 'James', NULL) 
INSERT INTO @Test VALUES (1, 2, NULL, 100.40) 
INSERT INTO @Test VALUES (1, 3, NULL, NULL) 
INSERT INTO @Test VALUES (2, 1, NULL, 80.50) 
INSERT INTO @Test VALUES (2, 2, 'Smith', NULL) 
INSERT INTO @Test VALUES (2, 3, NULL, NULL) 
INSERT INTO @Test VALUES (3, 1, 'Charles', NULL) 
INSERT INTO @Test VALUES (3, 2, NULL, NULL) 
INSERT INTO @Test VALUES (3, 3, 'Brown', NULL) 
INSERT INTO @Test VALUES (3, 4, NULL, 75) 
INSERT INTO @Test VALUES (4, 0, 'Ron', 50) 
INSERT INTO @Test VALUES (4, 1, NULL, 20) 
INSERT INTO @Test VALUES (4, 2, NULL, NULL) 

SELECT * FROM @Test 

@@ 2版 感謝GilM爲解決@@第1版。我已經做了一個小除了問題。 Seq列中的起始數字可以是0或1.在第一個問題的解決方案中,遞歸CTE中的錨是指1,如果它的1或0?在這個版本中,最後3行數據(GID = 4)被添加到所有上述三個代碼塊中。

謝謝!

+0

+1包括測試用例 – podiluska 2012-08-02 21:11:28

+0

KARTHIK - 你不覺得這個問題有點可笑時,你可以邀請埃裏克? – EBarr 2012-08-02 21:22:06

+0

你是否可以編輯併發布'@@ Version'以防萬一答案變成特定的? – EBarr 2012-08-02 21:30:23

回答

2

這個怎麼樣?:

;WITH CTE AS (
SELECT GID, SEQ, Name, Salary 
FROM @Test t1 
WHERE SEQ = (SELECT MIN(SEQ) FROM @Test t2 WHERE t2.GID = t1.GID) 
UNION ALL 
SELECT t.GID, t.SEQ, COALESCE(t.Name,c.Name), COALESCE(t.Salary,c.Salary) 
FROM CTE c 
JOIN @Test t ON t.GID = c.GID AND t.SEQ = c.SEQ+1 
) 
UPDATE t SET 
    Name = c.Name, 
    Salary = c.Salary 
FROM @Test t 
JOIN CTE c ON c.GID = t.GID AND c.Seq = t.SEQ 
+0

感謝GilM!你的解決方案有效,我知道它將會是一個遞歸的CTE,但沒有足夠的(但)寫它。我已經更新了這一點: '選擇GID,SEQ,姓名,工資 FROM @Test WHERE SEQ = 1' 要 '選擇\t T.GID,T.SEQ,姓名,工資 FROM \t @Test牛逼 JOIN \t(SELECT GID,MIN(Seq)Seq FROM @Test GROUP BY GID)S on T.GID = S.GID and T.Seq = S.Seq' – KShan 2012-08-03 04:43:47

+0

此更新旨在處理@@版本2我的問題。這是否有更好的選擇? – KShan 2012-08-03 04:57:48

+0

您的更新對我來說很不錯。 – GilM 2012-08-03 06:45:04

1
update T set 
    Name = (
      select top(1) T1.Name 
      from @Test as T1 
      where T1.GID = T.GID and 
       T1.Seq <= T.Seq and 
       T1.Name is not null 
      order by T1.Seq desc 
      ), 
    Salary = (
      select top(1) T1.Salary 
      from @Test as T1 
      where T1.GID = T.GID and 
       T1.Seq <= T.Seq and 
       T1.Salary is not null 
      order by T1.Seq desc 
      ) 
from @Test as T 
where T.Name is null or 
     T.Salary is null 

隨着50列將有大量的輸入和大量的相關子查詢。

這是一個使用XML代替的版本。減少打字和性能可以更好。

with C as 
(
    select GID, 
     (
     select * 
     from @Test as T2 
     where T1.GID = T2.GID 
     order by T2.Seq desc 
     for xml path('row'), type 
     ) as X 
    from @Test as T1 
    group by GID 
) 
update T set 
     Name = C.X.value('(/row[Seq<=sql:column("T.Seq")]/Name)[1]', 'varchar(50)'), 
     Salary = C.X.value('(/row[Seq<=sql:column("T.Seq")]/Salary)[1]', 'decimal') 
from @Test as T 
    inner join C 
    on T.GID = C.GID 

SE-Data

+0

我認爲這可以工作(還沒有測試過),但是由於OP說實際表格有超過50列,所以對每一列都有一個子查詢似乎不切實際。 – GilM 2012-08-03 06:48:04

+0

@GilM我同意50列*不切實際* :)。添加了一個允許較少打字的版本,並且可能表現更好。只要你能保證'Seq'確實是連續的,你的版本就可以正常工作。 – 2012-08-03 08:20:18

+0

不錯!我不知道如何執行,但它非常有創意。 – GilM 2012-08-03 13:52:45