web-dev-qa-db-ja.com

相互適用と外部適用の速度差

CROSS APPLYを使用してUsersテーブルとGeoPhoneテーブルを結合していましたが、すべて高速で動作していましたが、Phone列にNULL値を持つユーザーがいます。クロスアプライは、最終出力でこれらの行をスキップします。そこで、外部適用に切り替えました。ただし、動作が大幅に遅くなります(出力の行の総数が1000だけ増加した場合、15倍以上遅くなります)。

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users CROSS APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE dbo.Users.Phone <= dbo.GeoPhone.[End]) GeoPhone

対:

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users OUTER APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE dbo.Users.Phone <= dbo.GeoPhone.[End]) GeoPhone

理由を理解しようとしています。私が見るように、実行計画は異なります。しかし、理論的には、このようなスローダウンを引き起こす可能性のある計算を見ることはできません。

何か案は?

私の最終的な解決策:

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users CROSS APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE ISNULL(dbo.Users.Phone, 0) <= dbo.GeoPhone.[End]) GeoPhone

これにより、Null以外の電話には実際の国が、Null電話には最初の範囲の国が割り当てられます(私の場合は既に「不明」です)。何らかの理由で WHERE dbo.Users.Phone <= dbo.GeoPhone.[End] OR dbo.Users.Phone IS NULLは同じ結果を出しますが、非常に遅くなります。

これをコメントアウトしてお気軽に。

51
Denis

CROSS APPLYはMSSQL固有です... Microsoft on APPLY

APPLYにより、左側のクエリの結果ごとに右側のクエリが1回実行されます。 CROSSは、INNER JOINのような一致する行のみを考慮します。 OUTERを使用すると、左側のクエリのすべての行が考慮されます。余分な行が痛い。

OUTER APPLYを使用する代わりに、NULLを明示的に受け入れるように右側のクエリを再構築することをお勧めします。

73
Magicianeer

これを試すことができます:

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users CROSS APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE dbo.Users.Phone <= dbo.GeoPhone.[End]) GeoPhone
UNION ALL
SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, NULL AS Country
FROM  dbo.Users
WHERE dbo.Users.Phone IS NULL

Dbo.Users.Phoneにインデックスがあることを確認してください

11
A-K