SQL:在 UNION 查询中使用 Top 1 和 Order By

Posted

技术标签:

【中文标题】SQL:在 UNION 查询中使用 Top 1 和 Order By【英文标题】:SQL: Using Top 1 in UNION query with Order By 【发布时间】:2009-05-09 06:05:59 【问题描述】:

我有一张如下表

Rate Effective_Date
---- --------------
5.6  02/02/2009
5.8  05/01/2009
5.4  06/01/2009
5.8  12/01/2009
6.0  03/15/2009

我应该找到对当前日期及其之后有效的所有费率。所以要获得当前的有效利率,我使用

SELECT TOP 1 * from table 
where effective_date < '05/05/2009' 
order by effective date desc

查询当前日期之后的费率

SELECT * from table 
where effective_date > '05/05/2009'

为了结合这两个结果,我使用联合作为

SELECT TOP 1 * from table 
where effective_date < '05/05/2009' 
order by effective date desc

UNION

SELECT * from table 
where effective_date > '05/05/2009'

预期的结果是

Rate Effective Date
---- --------------
5.8  05/01/2009
5.4  06/01/2009
5.8  12/01/2009
6.0  03/15/2009

但我得到的实际结果是

Rate Effective Date
---- --------------
5.6  02/02/2009
5.4  06/01/2009
5.8  12/01/2009
6.0  03/15/2009

我不知道为什么会发生这种情况?有什么建议吗?

【问题讨论】:

我不确定您的“预期”结果实际上是您应该期待的结果。. 2009 年 3 月 15 日是您搜索前的“倒数第二个”“最新的上一个”记录“2009 年 5 月 5 日”的日期.. 您应该只在预期结果集中获得 3 条记录。 【参考方案1】:

它是这样工作的:

select *
from (
    select top 1 *
    from table
    where effective_date <= '05/05/2009'
    order by effective_date desc
) as current_rate

union all

select *
from table
where effective_date > '05/05/2009'

【讨论】:

这是否解决了 Union 查询中不尊重“top x”的问题? (我认为 itr 在 MSSS 中有但没有玩过) 是的,您的答案的编辑版本确实有效(将按查询的顺序包装在联合的非按查询中)。和猫一样,有十几种不同的方法可以给它们剥皮。我验证了您的查询以返回与我相同的结果。你的方法非常适合快速而肮脏的“立即获取数据”情况......我喜欢我描述的 CTE 方法,因为一旦它被写入,你就可以对数据做任何你想做的事情。 (即,给我第二个最前的费率 [但不是最前的] 和所有“当前”费率) 当我需要使用TOP并遇到“UNION ate my ORDER BY”问题时,我也更喜欢使用派生表方法。 FWIW 这部分对我运行 MS SQL 2019 有效。我必须将两个 SELECT 都放入子查询中,然后将两者合并。 SQL 2019 的播放方式不同,或者因为我的两个查询都是 TOP(10)..ORDER BY 查询。【参考方案2】:

作为联合的一部分的 select 语句中的 Order By 将被忽略。因此,您的 TOP 1 正在选择一些任意记录(可能是表的聚集键的第一条记录)。

【讨论】:

说得好。我以前遇到过这个确切的问题。【参考方案3】:

与 Union 一起使用时,Order By 无效...

我使用公用表表达式和一些 Rank 和 Case 语句技巧处理了一个快速而肮脏的东西,以获得您正在寻找的结果..

WITH CTE_RATES ( RATE, EFFECTIVE_DATE, CUR, SORT )
AS (
    SELECT 
        Rate,
        Effective_date,
        CASE WHEN Effective_date > '5/5/2009' THEN 1
             ELSE 0
        END,
        RANK() OVER (PARTITION BY
                         CASE WHEN EFFECTIVE_DATE > '5/5/2009' THEN 1
                              ELSE 0
                         END
                     ORDER BY EFFECTIVE_DATE DESC)
    FROM TestTable
)

SELECT RATE, EFFECTIVE_DATE
FROM (
    SELECT RATE, EFFECTIVE_DATE 
    FROM CTE_RATES 
    WHERE CUR = 0 AND SORT = 1

    UNION ALL

    SELECT RATE, EFFECTIVE_DATE
    FROM CTE_RATES
    WHERE CUR = 1
    ) AS QRY
ORDER BY EFFECTIVE_DATE

解释发生了什么...

CTE 定义查询返回的速率、日期、当前和排序标志...

CASE 将结果分为搜索日期之前的结果和搜索日期之后的结果。我们在联合中使用案例 (Cur) 的结果从分区列表中提取结果。 .

Rank() 函数然后通过根据 CASE 语句用于分隔列表的相同标准创建分区来对列表进行排序。然后我们以降序方式按生效日期排序。这将采用“过去”列表并使其成为最新的“过去”条目排名 1..

然后在查询的联合部分..

在顶部,我们从“过去”列表 (cur = 0) 和“过去”列表中的第一个条目中获取排名和日期.. (sort = 1).. 这将返回 1记录(如果没有搜索日期之前的记录,则为 0)..

然后我们将其与“当前”列表 (cur = 1) 中的所有记录合并

然后最后.. 我们采用 UNION 的结果.. 并在生效日期之前订购它,为我们提供所有当前记录以及“最新”的先前记录。

【讨论】:

【参考方案4】:

我相信上述查询通过使用 而不是 = 排除了 05/01/2009。

【讨论】:

以上是关于SQL:在 UNION 查询中使用 Top 1 和 Order By的主要内容,如果未能解决你的问题,请参考以下文章

LINQ体验——LINQ to SQL语句之Union All/Union/Intersect和Top/Bottom和Paging和SqlMethods

在 SQL 查询中一起使用 union 和 count(*)

在 sql 查询中使用 group/order by 和 union 子句

如何用union合并SQL查询的数据

SQL学习之组合查询(UNION)

sql中Union和union all的使用