带有where子句的前1个 - 如果没有找到记录,则忽略where子句

时间:2014-11-22 22:25:52

标签: sql sql-server sql-server-2008

按日期选择第一个未取消的活动的最佳方法是什么,如果取消所有活动以选择第一个活动。

带样本数据的方案:

create table SomeOtherTable
(
    Id bigint primary key
)

create table activities
(
  Id bigint identity(1,1) primary key,
  SomeForeignKey bigint,
  Description varchar(100),
  Date datetime,
  Canceled bit  
)


insert into SomeOtherTable values (1),(2),(3)

insert into activities values (1, 'Activity 1', '20141201', 1),
                              (1, 'Activity 2', '20141203', 0),
                              (1, 'Activity 3', '20141205', 0),
                              (2, 'Activity 4', '20141207', 1),
                              (2, 'Activity 5', '20141209', 1),
                              (3, 'Activity 6', '20141209', 0)

期望的输出:

  

活动2 - 2014/12/03 - 0

     

活动4 - 2014/12/07 - 1

     

活动6 - 2014/12/09 - 0

我目前正在使用此查询,但我认为必须有更好的解决方案......

select  case when a1.Id is null then a2.Description else a1.Description end as Description,
        case when a1.Id is null then a2.Date else a1.Date end as Date,
        case when a1.Id is null then a2.Canceled else a1.Canceled end as Canceled
from SomeOtherTable t
outer apply (select top 1 * 
             from activities a 
             where t.id=a.SomeForeignKey
             and a.Canceled = 0
             order by a.Date) a1

cross apply (select top 1 * 
             from activities a 
             where t.id=a.SomeForeignKey             
             order by a.Date) a2

SQL Fiddle link

1 个答案:

答案 0 :(得分:2)

您希望优先处理结果,首先是Canceled = 0,然后是任何其他行。您可以使用一个outer apply

执行此操作
select a.Description, a.Date, a.Canceled
from SomeOtherTable t outer apply
     (select top 1 * 
      from activities a 
      where t.id = a.SomeForeignKey
      order by (case when a.canceled = 0 then 1 else 0 end) desc, a.Date
     ) a;

我通常会在row_number()计算中加入类似的逻辑,但outer apply的效果也一样。

编辑:

为了完整性和比较,这里是row_number()方法:

select a.Description, a.Date, a.Canceled
from SomeOtherTable t left join
     (select a.*,
             row_number() over (partition by a.SomeForeignKey
                                order by (case when a.canceled = 0 then 1 else 0 end) desc, a.Date
                               ) as seqnum
      from activities a 
     ) a
     on t.id = a.SomeForeignKey and seqnum = 1;

一般来说,我认为apply方法要快一点 - 基本上,它会在第一个匹配行停止,并且不需要继续处理给定t.id