web-dev-qa-db-ja.com

結果の順序を個々のクエリでランク付けする必要がある2つのクエリの和集合から上位nを選択するにはどうすればよいですか?

ユーザー名のテーブルがあるとしましょう。

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

'Bob'の名前のn一致のリストを返したいのですが、結果のセットには最初に完全一致が含まれ、次に同様の一致が含まれます。

私はこのようなものがうまくいくかもしれないと思いました

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

しかし、2つの問題があります。

  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)

これは単一のクエリで可能ですか?

13
Jedidja

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
16
Andomar

元のクエリを少し変更すると、これが解決するはずです。 WHERE Name LIKE 'Bob%'に一致するUNIONを追加して、この優先度を2にすると、'%Bob'の優先度を3に変更すると、さらに優れた検索IMHOが得られます。

SELECT TOP 4 a.* FROM
(
    SELECT *, 1 AS Priority from Usernames WHERE Name = 'Bob'
    UNION
    SELECT *, 2 from Usernames WHERE Name LIKE '%Bob%'
) AS a
ORDER BY Priority ASC
3
Will A
SET ROWCOUNT 4

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

SET ROWCOUNt 0
2

これは私のために働きます:

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
2
Daniel Williams

これにより、パフォーマンスが向上します。

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
1
Mikael Eriksson

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')

これは私の最終的なクエリの縮小版であるため、実際には意味がありませんが、アイデアを得る必要があります。

0
Ads