首页 > 解决方案 > 使用 Union 和 Order By 而不重复

问题描述

我正在使用 anUNION来合并精确搜索和模糊搜索的结果。我希望完全匹配位于顶部,其他结果按列排序。我发现这个解决方案可以正常工作UNION ALL,但是通过添加rank列我失去了UNION(没有全部)的属性以从结果集中删除完全匹配的重复项。

有没有一种优雅的方法来解决这个问题,还是我必须手动删除重复项?

作为参考,我的简化查询:

SELECT 1 AS [Rank], [CallerID] 
FROM [PHONE]
WHERE [CallerID] = '12345'

UNION

SELECT 2 AS [Rank], [CallerID] 
FROM [PHONE]
WHERE [CallerID] LIKE '12%' AND ABS(LEN([CallerID]) - LEN('12345')) < 3
ORDER BY [Rank] ASC, [CallerID] ASC

结果可能如下所示:

Rank        CallerID
----------- --------------------
1           12345
2           123
2           1233
2           1234
2           12345     <- I don't want this line
2           1236

备注:设置DISTINCT我的来电显示不会解决问题,因为我的真实查询有更多列。我真的只想删除我通过UNION.

标签: sqlsql-servertsqlunion

解决方案


将您现有的查询放在 CTE 中(这里我将您的示例数据放在那里),然后使用 aROW_NUMBER()和 a 进一步WHERE过滤结果:

with OriginalQuery as (
select 1 as Rank,  12345 as CallerID union all
select 2 ,123 union all
select 2,1233 union all
select 2,1234 union all
select 2,12345 union all
select 2,1236
), Preferred as (
    select *,ROW_NUMBER() OVER (
        PARTITION BY CallerID /* other columns too? */
        ORDER BY RANK
        ) as rn
    from OriginalQuery
)
select
    *
from
    Preferred
where
    rn = 1
order by Rank,CallerID

如前所述,对于此数据,您可能必须单独向PARTITIONifCallerID不是键添加更多/调整列。


当然,如果您的基础数据中没有任何重复项,并且您获得重复项的原因仅仅是因为您正在运行两次搜索并组合结果,那么执行起来要简单得多:

SELECT [CallerID] 
FROM [PHONE]
WHERE
    CallerID = '12345' OR
    ([CallerID] LIKE '12%' AND ABS(LEN([CallerID]) - LEN('12345')) < 3)
ORDER BY CASE WHEN CallerID='12345' THEN 0 ELSE 1 END, [CallerID] ASC

您将这两个搜索结合起来,而不是结合它们的结果,然后用于CASEORDER BY.


推荐阅读