在MSSQL 2008中合并行,

时间:2011-01-18 22:52:24

标签: sql sql-server tsql sql-server-2008 coalesce

我正在尝试确定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的MAX

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

结果如下:

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

它就像一个Coalesce而是在行上,而不是两个值/列。

还有很多其他列也应用了这个类似的方法,所以我真的不想为每个列进行单独的连接。

有什么想法吗?

5 个答案:

答案 0 :(得分:5)

我可能会使用相关的子查询。

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 :(得分:1)

这个例子应该有帮助,使用分析函数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将每条记录作为ID的子节点。 Null列已被省略,因此使用xpath(child / columnname)找到的第一列是第一个类似于COALESCE的非null值。

答案 2 :(得分:0)

您可以使用子查询:

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

答案 3 :(得分:0)

我创建了一个名为#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

答案 4 :(得分:0)

假设一个名为“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