SQL 服务器:如何有效地 return 两个略有不同的查询的组合结果集

SQL Server: How to efficiently return the combined result set of two slightly different queries

我已经研究了一段时间,虽然有一些 "brute force" 方法在技术上可行,但我觉得我缺少一些更优雅(和高效)的方法。

我有一个 table 包含事件的历史。对于每个事件,都有一个 "source" 和一个 "destination"(这就是我要过滤的内容)。我需要 return 按日期排序的前 n 行(最近的第一行),首先是来自特定来源的给定目的地,然后是除第一个使用的来源之外的任何来源的相同目的地询问。例如,如果第一个查询(匹配目标和源)returns n 行,我不需要第二个查询中的任何行(匹配相同的目标并匹配任何 other 源),但是如果第一个查询 return 少于 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 服务器是否会自动停止 运行第一个(或第二个)子查询一旦满足外部查询中的 TOP (100)(这将是理想的)。

下面的示例使用 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 列上的 clustered/covering 索引将有助于提高性能。它不如 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;