3

次のようにユーザーを検索すると:

SELECT *
FROM userprofile
WHERE userid IN (1, 2, 3)

実行計画は、UserProfile が Clustered Index Seek を使用していることを示しています

サブクエリを使用するように IN 句を変更すると、次のようになります。

SELECT *
FROM userprofile
WHERE userid IN (
    SELECT DISTINCT senders.UserId
    FROM messages m
    JOIN UserMessages recipients ON recipients.MessageId = m.MessageId
    JOIN UserMessages senders ON senders.MessageId = m.MessageId
    WHERE recipients.TypeId = 2
        AND recipients.UserId = 1
        AND senders.UserId <> 1
        AND senders.TypeId = 1 
)

実行計画は、サブクエリが Clustered Index Seek を使用していることを示していますが、UserProfile 外部クエリは Clustered Index Scan を使用しています。

内部クエリと外部クエリの両方がシークを使用するようにするにはどうすればよいですか?

4

2 に答える 2

4

行数が少ない場合、一連のシークはフル スキャンよりも安価です。SQL Server はかなり保守的であるため、多くのレコードが見つかる可能性がある場合は、スキャンを優先します。あなたの例では、userId in (1,2,3)多くの行が返されないことは明らかです。しかし、サブクエリでは、SQL Server はおそらく判断できません。

次のコマンドでシークを強制できます。

from userprofile with (forceseek)
于 2013-05-09T16:04:24.847 に答える
1

シークを強制しても必ずしも速くなるとは限らず、大幅に遅くなる可能性があります。

IN の代わりに EXISTS を使用するようにクエリを書き直すと、より高速になる可能性があります。

select * from userprofile    -- << note that '*' is slow here
where EXISTS(
  select * from messages m   -- << but '*' is not slow here
  join UserMessages recipients on recipients.MessageId = m.MessageId
  join UserMessages senders on senders.MessageId = m.MessageId
  where recipients.TypeId = 2
    and recipients.UserId = 1 
    and senders.UserId <> 1 
    and senders.TypeId = 1
    and senders.UserId = userprofile.userid
)

それは確かに遅くなりません。

于 2013-05-09T16:13:17.570 に答える