我在SQL Server 2014中有一个大型SQL查询,其中包含许多列和连接,并在下面对其进行了简化。目标表的连接涉及日期范围,如果存在重叠的日期范围,则可能会链接两个或多个记录。如果发生这种情况,我只想随意选择一个,如最小值或第一个。没关系。如果没有Group By,有没有一种好方法可以做到这一点?换句话说,没有在a.ID上执行Group By然后在GoalValue上执行MIN?现实生活场景涉及避免数据库中的错误数据。如果必须的话,我会求助于Group By。
select a.ID, g.GoalValue
from Table1 a
join Table2 b on a.ID = b.ID
left join Goals g on g.ID = b.ID
and b.BeginDate between g.StartDate and g.EndDate
Sample Data for Goals table:
ID | GoalValue | StartDate | EndDate
1 5 1/1/2000 12/31/2000
1 6 6/1/2000 9/1/2000
1 10 1/1/2001 12/31/2009
1 12 1/1/2010 12/31/2050
2 100 1/1/2000 12/31/2050
3 50 1/1/2000 12/31/2005
3 75 1/1/2006 12/31/2050
如果ID = 1且BeginDate值为8/1/2000,则将返回值为5和6的记录。但我只想要一排。一个记录是我可能无法在短期内删除的错误数据。
答案 0 :(得分:1)
你可以使用" TOP 1" (T-SQL)或" LIMIT 1"您查询中的(大多数其他DBMSes)子句。然后引擎将返回它遇到的第一行。
T-SQL:
select TOP 1 a.ID, g.GoalValue
from Table1 a
join Table2 b on a.ID = b.ID
left join Goals g on g.ID = b.ID
and b.BeginDate between g.StartDate and g.EndDate
Oracle,MySql,Postgre等:
select a.ID, g.GoalValue
from Table1 a
join Table2 b on a.ID = b.ID
left join Goals g on g.ID = b.ID
and b.BeginDate between g.StartDate and g.EndDate
LIMIT 1
根据引擎的查询计划和任何其他条件或条款,这将为您提供相当确定的结果。
答案 1 :(得分:1)
至少有两种方法可以实现这一点。
这是我的模拟架构和数据,因为您没有提供任何:
declare @Tbl table (
Id int primary key,
BeginDate date not null
);
declare @Goals table (
Id int not null,
GoalValue int not null,
StartDate date not null,
EndDate date not null
);
insert into @Tbl (Id, BeginDate)
values
(1, '20150101'),
(2, '20160101'),
(3, '20170101'),
(4, '20170224');
insert into @Goals (Id, GoalValue, StartDate, EndDate)
values
(1, 25, '20141015', '20151102'),
(1, 74, '20141231', '20151111'),
(2, 182, '20150704', '20151123'),
(3, 11, '20160701', '20170630'),
(4, 248, '20160701', '20170630'),
(4, 15, '20160701', '20170209'),
(4, 7, '20170101', '20180101');
T-SQL中有一个非常巧妙的技巧,可以从多个组中获取第一行,而不会使用子查询或聚合来混淆代码:with ties
子句的top
选项。它返回所有具有相同排序条件值的行(在我们的例子中,它们首先在它们的组中):
select top (1) with ties t.Id, g.GoalValue
from @Tbl t
left join @Goals g on t.Id = g.Id and t.BeginDate between g.StartDate and g.EndDate
order by row_number() over(partition by t.Id order by g.StartDate);
然而,这个解决方案并不灵活;特别是,您可以在不将其嵌套到子查询中的情况下对其进行排序。就个人而言,我更喜欢相关性:
select t.Id, oa.GoalValue
from @Tbl t
outer apply (
select top (1) g.GoalValue from @Goals g
where g.Id = t.Id
and t.BeginDate between g.StartDate and g.EndDate
order by g.StartDate
) oa;
根据您是否需要没有范围匹配的行,您可以在outer
的{{1}}和cross
版本之间切换;查询的其余部分保持不变。此外,它可以让您完全自由地定义匹配条件,以防您稍后再提出任何标准。
答案 2 :(得分:0)
使用 row_number()
对每个partition
的重新开始的行进行编号。在这种情况下,如果我们按b.Id, b.BeginDate
进行分区,则每次重复该行(跨goal
个日期范围)时,它会递增1.我们可以使用where rn=1
删除所有这些额外匹配像这样:
select Id, GoalValue
from (
select a.ID, g.GoalValue
, rn = row_number() over (
partition by b.Id, b.BeginDate
order by g.StartDate
)
from Table1 a
join Table2 b on a.ID = b.ID
left join Goals g on g.ID = b.ID
and b.BeginDate between g.StartDate and g.EndDate
)
where rn = 1
答案 3 :(得分:0)
我喜欢@KeithS使用TOP 1的答案。如果你真的不关心选择哪一行,那么TOP 1应该提供最高效的结果。但是我觉得给出的示例更难以推断为更复杂的查询,并且不允许您在联合列表中使用聚合。
如果问题出在连接表中,您只需要1行,那么您只需要在该连接表上指定TOP 1,而不是整个记录集。
您可以使用嵌套选择来实现此目的但是我发现Apply语句的语法更清晰:
select TOP 1 a.ID, g.GoalValue
from Table1 t1
join Table2 t2 on t1.ID = t2.ID
outer apply (
SELECT TOP 1 GoalValue
FROM Goals
WHERE Goals.ID = t2.ID
and t2.BeginDate between Goals.StartDate and Goals.EndDate
) g
请注意,apply对嵌套表的评估方式不同,并且取决于where子句,它可能不如group by或嵌套查询有效。 Apply最适合包含内联函数,并确保它们在记录集中每行执行一次,这会使它们在基于集合的逻辑中性能降低。如果使用Apply并且期望获得最佳结果,请尝试不过滤apply语句返回的字段,或尝试过滤尽可能多的行,然后过滤apply中的结果字段。在此示例中,要么不在GoalValue上包含过滤器,要么必须对GoalValue进行过滤,请将其作为where条件中的最后一个条件 请参阅MSDN上的"Using APPLY"
注意:在应用查询中,您可以自由使用聚合,例如AVE(g.GoalValue)而不是TOP 1,而无需指定分组语句。
警告:如本文所述,不要在子查询中使用Row_Number()或其他窗口技术来获取第一行或计算平均值,这不会增加任何额外的值而不是简单的top 1,它使语法复杂化并对性能产生影响。
outer apply (
SELECT AVE(GoalValue)
FROM Goals
WHERE Goals.ID = t2.ID
and t2.BeginDate between Goals.StartDate and Goals.EndDate
) g
以下是使用嵌套选择进行比较的相同查询
select TOP 1 a.ID, g.GoalValue
from Table1 t1
join Table2 t2 on t1.ID = t2.ID
left join (
SELECT TOP 1 GoalValue
FROM Goals
WHERE Goals.ID = t2.ID
and t2.BeginDate between Goals.StartDate and Goals.EndDate
) g
即使语法几乎相同,这两个查询的执行计划也会大不相同。您提到您的特定查询比您发布的内容更复杂,因此试用了此处列出的内容的一些变体,并让我们知道哪一个最适合您的方案!