Объединение строк в MSSQL 2008, - PullRequest
4 голосов
/ 19 января 2011

Я пытаюсь определить лучший подход здесь, в MSSQL 2008.

Вот мои образцы данных

TransDate  Id     Active
-------------------------
1/18 1pm   5      1    
1/18 2pm   5      0    
1/18 3pm   5      Null    
1/18 4pm   5      1    
1/18 5pm   5      0    
1/18 6pm   5      Null

Если сгруппированы по Id и упорядочены по TransDate, я хочу получить последнее ненулевое значение для активного столбца и МАКС. Из TransDate

SELECT MAX(TransDate) AS TransDate, 
       Id,
       --LASTNonNull(Active) AS Active

Вот результаты:

TransDate  Id  Active
---------------------    
1/18 6pm   5   0

Это будет похоже на Coalesce, но поверх строк вместо двух значений / столбцов.

Было бы много других столбцов, к которым также применялся бы подобный метод, поэтому я действительно не хочу делать отдельное соединение для каждого из столбцов.

Есть идеи?

Ответы [ 5 ]

5 голосов
/ 19 января 2011

Я бы, вероятно, использовал коррелированный подзапрос.

SELECT MAX(TransDate)             AS TransDate,
       Id,
       (SELECT TOP (1) Active
        FROM   T t2
        WHERE  t2.Id = t1.Id
               AND Active IS NOT NULL
        ORDER  BY TransDate DESC) AS Active
FROM   T t1
GROUP  BY Id  

Способ без

SELECT
    Id,
    MAX(TransDate) AS TransDate,
    CAST(RIGHT(MAX(CONVERT(CHAR(23),TransDate,121) + CAST(Active AS CHAR(1))),1) AS BIT) AS Active,
    /*You can probably figure out a more efficient thing to 
    compare than the above depending on your data. e.g.*/
    CAST(MAX(DATEDIFF(SECOND,'19500101',TransDate) * CAST(10 AS BIGINT) + Active)%10  AS BIT) AS Active2
FROM T
GROUP BY Id

Или после комментариев cross apply будет работать лучше для вас?

WITH T (TransDate, Id, Active, SomeOtherColumn) AS
(
select GETDATE(), 5, 1, 'A' UNION ALL
select 1+GETDATE(), 5, 0, 'B' UNION ALL
select 2+GETDATE(), 5, null, 'C' UNION ALL
select 3+GETDATE(), 5, 1, 'D' UNION ALL
select 4+GETDATE(), 5, 0, 'E' UNION ALL
select 5+GETDATE(), 5, null,'F'

),
T1 AS
(
SELECT MAX(TransDate) AS TransDate,
       Id
FROM   T
GROUP  BY Id  
)
SELECT T1.TransDate,
       Id,
       CA.Active AS Active,
       CA.SomeOtherColumn AS SomeOtherColumn
FROM   T1
CROSS APPLY (SELECT TOP (1) Active, SomeOtherColumn
        FROM   T t2
        WHERE  t2.Id = T1.Id
               AND Active IS NOT NULL
        ORDER  BY TransDate DESC) CA
1 голос
/ 19 января 2011

Этот пример должен помочь, используя аналитические функции Max () OVER и Row_Number () OVER

create table tww( transdate datetime, id int, active bit)
insert tww select GETDATE(), 5, 1
insert tww select 1+GETDATE(), 5, 0
insert tww select 2+GETDATE(), 5, null
insert tww select 3+GETDATE(), 5, 1
insert tww select 4+GETDATE(), 5, 0
insert tww select 5+GETDATE(), 5, null

select maxDate as Transdate, id, Active
from (
    select *,
        max(transdate) over (partition by id) maxDate,
        ROW_NUMBER() over (partition by id
                order by case when active is not null then 0 else 1 end, transdate desc) rn
    from tww
) x
where rn=1

Другой вариант, довольно дорогой, будет делать это через XML. Только для образовательных целей

select
    ID = n.c.value('@id', 'int'),
    trandate = n.c.value('(data/transdate)[1]', 'datetime'),
    active = n.c.value('(data/active)[1]', 'bit')
from
(select xml=convert(xml,
    (select id [@id],
        (   select *
            from tww t
            where t.id=tww.id
            order by transdate desc
            for xml path('data'), type)
    from tww
    group by id
    for xml path('node'), root('root'), elements)
)) x cross apply xml.nodes('root/node') n(c)

Он работает по принципу, что сгенерированный XML имеет каждую запись в качестве дочернего узла идентификатора. Пустые столбцы опущены, поэтому первый столбец, найденный с использованием xpath (child / columnname), является первым ненулевым значением, аналогичным COALESCE.

0 голосов
/ 19 января 2011

Если предположить таблицу с именем "test1", как насчет использования ROW_NUMBER, OVER и PARTITION BY?

SELECT transdate, id, active FROM 
    (SELECT transdate, ROW_NUMBER() OVER(PARTITION BY id ORDER BY transdate desc) AS rownumber, id, active  
     FROM test1
     WHERE active is not null) a 
WHERE a.rownumber = 1
0 голосов
/ 19 января 2011

Я создал временную таблицу с именем #temp для проверки своего решения, и вот что я придумал:

transdate              id  active
1/1/2011 12:00:00 AM    5   1
1/2/2011 12:00:00 AM    5   0
1/3/2011 12:00:00 AM    5   null
1/4/2011 12:00:00 AM    5   1
1/5/2011 12:00:00 AM    5   0
1/6/2011 12:00:00 AM    5   null
1/1/2011 12:00:00 AM    6   2
1/2/2011 12:00:00 AM    6   3
1/3/2011 12:00:00 AM    6   null
1/4/2011 12:00:00 AM    6   2
1/5/2011 12:00:00 AM    6   null

Этот запрос ...

select max(a.transdate) as transdate, a.id, (
  select top (1) b.active
  from #temp b
  where b.active is not null
  and b.id = a.id
  order by b.transdate desc
) as active
from #temp a
group by a.id

Возвращает этирезультаты.

transdate              id  active
1/6/2011 12:00:00 AM    5   0
1/5/2011 12:00:00 AM    6   2
0 голосов
/ 19 января 2011

Вы можете использовать подзапрос:

SELECT MAX(TransDate) AS TransDate
,      Id
,      (
       SELECT  TOP 1 t2.Active 
       FROM    YourTable t2
       WHERE   t1.id = t2.id
               and t2.Active is not null 
       ORDER BY 
               t2.TransDate desc
       )
FROM   YourTable t1
...