2015-02-26 39 views
1

我有這樣一個表,SQL Server的行合併,並獲得最新值

Id A  B  C  D  touchedwhen 
1 NULL yes  NULL yes 2015-02-26 14:10:01.870 
1 NULL NULL no  no 2015-02-26 14:10:40.370 

,並需要對它們合併到一排這樣的,

Id A  B  C  D  touchedwhen 
1 NULL yes  no  no 2015-02-26 14:10:40.370. 

注:如果值存在在兩行採取的最新一個日期..

嘗試這樣的查詢:

select id, 
max(a), 
max(b), 
max(c), 
max(d), -- data in both rows hence take the latest 
max(touchedwhen) 
from 
[dbo].[Table_1] 
group by id; 
+1

你希望用相同的ID超過2行? – user1455836

回答

0

如果你只是在尋找每個ID的最後一個值,你可以使用:

last_value(a) over(
    partition by id 
    order by touchedwhen desc 
    rows between unbounded preceding and unbounded following) as a 

但你正在尋找每id的最後一個值是not null。我能想出的唯一事情是由四部分組成加入,每個子查詢a, b, c, d計算最新的非空值:

select ids.id 
,  a.a 
,  b.b 
,  c.c 
,  d.d 
,  ids.tw 
from (
     select id 
     ,  max(touchedwhen) as tw 
     from YourTable 
     group by 
       id 
     ) ids 
left join 
     (
     select row_number() over (
        partition by id 
        order by touchedwhen desc) rn 
     ,  a 
     ,  id 
     from YourTable 
     where a is not null 
     ) a 
on  a.id = ids.id 
     and a.rn = 1 
left join 
     (
     select row_number() over (
        partition by id 
        order by touchedwhen desc) rn 
     ,  b 
     ,  id 
     from YourTable 
     where b is not null 
     ) b 
on  b.id = ids.id 
     and b.rn = 1 
left join 
     (
     select row_number() over (
        partition by id 
        order by touchedwhen desc) rn 
     ,  c 
     ,  id 
     from YourTable 
     where c is not null 
     ) c 
on  c.id = ids.id 
     and c.rn = 1 
left join 
     (
     select row_number() over (
        partition by id 
        order by touchedwhen desc) rn 
     ,  d 
     ,  id 
     from YourTable 
     where d is not null 
     ) d 
on  d.id = ids.id 
     and d.rn = 1 
+0

我會去四次'CROSS APPLY ... TOP(1)... ORDER BY ...'而不是四次'ROW_NUMBER'。對於'(id,touchedwhen desc)'的索引,它可能更有效率 - 對於每個ID,每個'CROSS APPLY'應該執行一次索引查找,而'ROW_NUMBER'將掃描整個表。 –

+0

@VladimirBaranov:SQL Server優化器在上面的查詢中有更多的自由(特別是沒有'top 1')。嘗試一下。 – Andomar

0
-- Get all latest values in one row for each primary key 
WITH CTE(row_num, Id, A, B, C, D, touchedwhen) AS (
    SELECT ROW_NUMBER() OVER(PARTITION BY Id ORDER BY touchedwhen DESC), Id, A, B, C, D FROM Table_1) 
UPDATE CTE SET 
    A = (SELECT TOP 1 t.A FROM Table_1 t WHERE t.A IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC), 
    B = (SELECT TOP 1 t.B FROM Table_1 t WHERE t.B IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC), 
    C = (SELECT TOP 1 t.C FROM Table_1 t WHERE t.C IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC), 
    D = (SELECT TOP 1 t.D FROM Table_1 t WHERE t.D IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC) 
    WHERE row_num = 1 

-- Delete extra rows per primary key after copying latest values to one row 
WITH CTE(row_num) AS (
    SELECT ROW_NUMBER() OVER(PARTITION BY Id ORDER BY touchedwhen DESC) FROM Table_1) 
DELETE FROM CTE WHERE row_num > 1