我已经玩了一段时间了,虽然有一些"蛮力"在技术上工作的方法,我觉得我错过了一些更优雅(和更有效)的东西。
我有一个包含事件历史记录的表。对于每个活动,都有一个"来源"和目的地" (这就是我过滤的内容)。我需要返回按日期(最近的第一个)排序的前n行,首先是来自特定源的给定目标,然后是针对除第一个查询中使用的源之外的任何源的相同目标。例如,如果第一个查询(匹配目标和源)返回n行,我不需要第二个查询中的任何行(匹配相同的目标并匹配任何其他源),但是如果第一个查询返回少于n行,我需要用第二个查询填充其余的(假设有这样的行)。
使用UNION ALL不起作用,因为单个查询不能单独排序,因此我得到的结果集包含" any"的前n个。给定目标的源(如果我可以在每个单独的查询中包含ORDER BY,这可能是一个合理的解决方案):
SELECT TOP (100) * FROM
(
SELECT TOP (100) Destin, Source, OtherData, Timestamp
FROM History
WHERE Destin = @Destin
AND Source = @source
UNION ALL
SELECT TOP (100) Destin, Source, OtherData, Timestamp
FROM History
WHERE Destin = @Destin
AND Source <> @source
ORDER BY Timestamp DESC
) AS SubQuery
ORDER BY TimeStamp DESC
我也尝试过CTE,但在这方面没有找到更好的东西。关于替代方法的任何想法?
PS - 子查询中的TOP(100)是尝试提高性能,但我无法确定SQL Server是否会在满足TOP后自动停止运行第一个(或第二个)子查询( 100)在外部查询(这将是理想的)。
答案 0 :(得分:2)
以下示例使用CASE表达式提供所需的序列。 destin上的聚簇(或覆盖)索引将有助于优化此查询。这将执行单个搜索。
SELECT TOP(100) Destin
, Source
, OtherData
, Timestamp
, CASE WHEN Source = @source THEN 1
ELSE 2
END AS seq
FROM History
WHERE Destin = @Destin
ORDER BY seq
, Timestamp DESC;
修改强>
下面是另一种技术,如果典型情况对于同一目的地而言明显多于100行,则可能表现得更好。像Aaron建议的ORDER BY列上的聚簇/覆盖索引将有助于提高性能。它不像CASE表达方法那样优雅,需要2个搜索运算符,但如果性能比可维护性更重要,则可以选择。
WITH same_source
AS ( SELECT Destin
, Source
, OtherData
, Timestamp
, 1 AS seq
, ROW_NUMBER() OVER ( ORDER BY Destin, Source, Timestamp ) AS row_num
FROM dbo.History
WHERE Destin = @Destin
AND Source = @Source
) ,
different_source
AS ( SELECT Destin
, Source
, OtherData
, Timestamp
, 2 AS seq
, ROW_NUMBER() OVER ( ORDER BY Destin, Source, Timestamp ) AS row_num
FROM dbo.History
WHERE Destin = @Destin
AND Source <> @Source
)
SELECT TOP ( 100 )
Destin
, Source
, OtherData
, Timestamp
FROM ( SELECT Destin
, Source
, OtherData
, Timestamp
, seq
FROM same_source
WHERE row_num <= 100
UNION ALL
SELECT Destin
, Source
, OtherData
, Timestamp
, seq
FROM different_source
WHERE row_num <= 100
) AS test
ORDER BY seq
, Destin
, Source
, Timestamp;