Как выбрать первые n из объединения двух запросов, где результирующий порядок должен быть ранжирован по отдельному запросу? - PullRequest
13 голосов
/ 26 мая 2011

Допустим, у меня есть таблица с именами пользователей:

Id  |  Name
-----------
1   |  Bobby
20  |  Bob
90  |  Bob
100 |  Joe-Bob
630 |  Bobberino
820 |  Bob Junior

Я хочу вернуть список n совпадений по имени для 'Bob', где результирующий набор сначала содержит точные совпадения, а затем аналогичные совпадения.

Я думал, что-то подобное может сработать

SELECT TOP 4 a.* FROM
(
    SELECT * from Usernames WHERE Name = 'Bob'
    UNION
    SELECT * from Usernames WHERE Name LIKE '%Bob%'
) AS a

но есть две проблемы:

  1. Это неэффективный запрос, поскольку вложенный выбор может вернуть много строк (просмотр плана выполнения показывает, что объединение происходит до начала)
  2. (Почти), что более важно, точное совпадение (я) не будет отображаться первым в результатах, так как результирующий набор упорядочен по первичному ключу.

Я ищу запрос, который вернет (для TOP 4)

Id | Name
---------
20 | Bob
90 | Bob

(and then 2 results from the LIKE query, e.g. 1 Bobby and 100 Joe-Bob)

Возможно ли это в одном запросе?

Ответы [ 6 ]

16 голосов
/ 26 мая 2011

Вы можете использовать case, чтобы поместить точные совпадения сверху:

select  top 4 *
from    Usernames
where   Name like '%Bob%'
order by
        case when Name = 'Bob' then 1 else 2 end

Или, если вы беспокоитесь о производительности и имеете индекс на (Name):

select  top 4 *
from    (
        select  1 as SortOrder
        ,       *
        from    Usernames
        where   Name = 'Bob'
        union all
        select  2
        ,       *
        from    Usernames
        where   Name like  '%Bob%'
                and Name <> 'Bob'
                and 4 >
                (
                select  count(*)
                from    Usernames
                where   Name = 'Bob'
                )
        ) as SubqueryAlias
order by
        SortOrder
3 голосов
/ 26 мая 2011

Небольшое изменение вашего исходного запроса должно решить эту проблему.Вы можете добавить дополнительный UNION, который соответствует WHERE Name LIKE 'Bob%' и присвоить этому приоритету 2, изменив приоритет '%Bob' на 3, и вы получите еще лучший поиск по IMHO.

2 голосов
/ 02 октября 2012
SET ROWCOUNT 4

SELECT * from Usernames WHERE Name = 'Bob'
UNION
SELECT * from Usernames WHERE Name LIKE '%Bob%'

SET ROWCOUNt 0
1 голос
/ 26 мая 2011

Это работает для меня:

SELECT TOP 4 * FROM (
SELECT 1 as Rank , I, name  FROM Foo  WHERE Name = 'Bob' 
UNION ALL
SELECT 2 as Rank,i,name  FROM Foo  WHERE Name LIKE '%Bob%' 
) as Q1
ORDER BY Q1.Rank, Q1.I
1 голос
/ 26 мая 2011

Это может сделать то, что вы хотите с лучшей производительностью.

SELECT TOP 4 a.* FROM
(
    SELECT TOP 4 *, 1 AS Sort from Usernames WHERE Name = 'Bob'
    UNION ALL
    SELECT TOP 4 *, 2 AS Sort from Usernames WHERE Name LIKE '%Bob%' and Name <> 'Bob'
) AS a
ORDER BY Sort
0 голосов
/ 03 сентября 2014

Ответ от Will A заставил меня переступить черту, но я хотел бы добавить небольшое замечание, что если вы пытаетесь сделать то же самое и включить «FOR XML PATH», вам нужно написать это немногопо-разному.

Я указывал атрибуты XML и имел такие вещи, как:

SELECT Field_1 as [@attr_1]

Что нужно сделать, это удалить символ «@» в подзапросах, а затем добавить их обратно вс внешним запросом.Например:

SELECT top 1 a.SupervisorName as [@SupervisorName]
FROM
(
    SELECT (FirstNames + ' ' + LastName) AS [SupervisorName],1 as OrderingVal
    FROM ExamSupervisor SupervisorTable1

    UNION ALL

    SELECT (FirstNames + ' ' + LastName) AS [SupervisorName],2 as OrderingVal
    FROM ExamSupervisor SupervisorTable2

) as a
ORDER BY a.OrderingVal ASC
FOR XML PATH('Supervisor')

Это урезанная версия моего окончательного запроса, так что в действительности это не имеет смысла, но вы должны понять.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...