2015-02-26 2 views
1

У меня есть таблица, как это,сервер SQL слияния строк и получить последнее значение

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

вы ожидаете более 2-х строк с одинаковым идентификатором? – user1455836

ответ

0

Если вы просто искали последнее значение в ид, вы можете использовать:

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)' он может быть намного более эффективным - для каждого идентификатора каждый «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 
Смежные вопросы