2009-04-14 17 views
1

無効な国、地域、または地域IDを持つデータベースエントリを選択したい場合があります。これは、国や地域、または存在しなくなった領域のIDを意味します。テーブル、私は4つのテーブル:プロパティ、国、地域、エリアがあります。 私はこのようにそれを行うには考えていた:乱雑なSQL文

SELECT * FROM Properties WHERE 
Country_ID NOT IN 
(
SELECT CountryID FROM Countries 
) 
OR 
RegionID NOT IN 
(
SELECT RegionID FROM Regions 
) 
OR 
AreaID NOT IN 
(
SELECT AreaID FROM Areas 
) 

を今、私の質問権利がありますか?あなたが私がやることができ、より良いパフォーマンスで同じ結果を達成することをお勧めしますか?

答えて

4

実際にはクエリが最適です。

LEFT JOINは、他の人によって提案されていますが、すべての値を選択してフィルタリングするので悪化します。

おそらくあなたのサブクエリは、このように最適化されます

:あなたが使用する必要があります

SELECT * 
FROM Properties p 
WHERE NOT EXISTS 
     (
     SELECT 1 
     FROM Countries i 
     WHERE i.CountryID = p.CountryID 
     ) 
     OR 
     NOT EXISTS 
     (
     SELECT 1 
     FROM Regions i 
     WHERE i.RegionID = p.RegionID 
     ) 
     OR 
     NOT EXISTS 
     (
     SELECT 1 
     FROM Areas i 
     WHERE i.AreaID = p.AreaID 
     ) 

、。

このクエリでは、各テーブルから最大1つの行を選択し、次の繰り返しにジャンプします(つまり、特定のプロパティに対してCountryが見つからない場合は、Region )。

また、SQL Serverは、このクエリと元のプランに対して同じ計画を作成するのに十分スマートです。

更新:各テーブルに512K行でテスト

対応するIDの寸法表はCLUSTERED PRIMARY KEYです。Propertiesのすべてのメジャーフィールドがインデックスに登録されています。

PropertyPropertyID = CountryID = RegionID = AreaIDの行ごとに、実際に行が欠落していない(実行時間の点で最悪の場合)。

 
NOT EXISTS 00:11 (11 seconds) 
LEFT JOIN  01:08 (68 seconds) 
+0

私はネガティブではなくポジティブな検索を使用する点であなたのソリューションが気に入っていますが、アプローチ間のパフォーマンスの違いを確認することに興味があります。 – cjk

3

次のように異なり、それを書き換えることができます:

SELECT p.* 
FROM Properties p 
LEFT JOIN Countries c ON p.Country_ID = c.CountryID 
LEFT JOIN Regions r on p.RegionID = r.RegionID 
LEFT JOIN Areas a on p.AreaID = a.AreaID 
WHERE c.CountryID IS NULL 
OR r.RegionID IS NULL 
OR a.AreaID IS NULL 

テストの性能差を(いずれかが存在する場合 - それはする必要があるため、NOT INは、特に多くの項目の上に、厄介な検索であるとしてそこにする必要があります毎日1つずつテストしてください)。

検索するIDSを索引付けすることで、これを高速化することもできます。マスター表(国、地域、地域)ごとに、主キーをクラスタ化する必要があります。

+0

AFAIKでは、通常、この例ではクラスタ化されていないとクラスタ化されていないとの違いはほとんどありません。 –

+0

@Marc Gravell、確かにほとんど無視できるでしょう。私はほとんどのテーブルのClustered Indexesが好きです。 – cjk

3

これはcleanup sqlと思われるので、これは問題ありません。でも、次回は気にしないようにforeign keysをどうやって使うのですか?

+0

私の考えは正確です。クエリは正しい結果を返し、結果を使用してデータをクリーンアップしてから外部キーを追加するため、非効率な場合は問題ありません。 –

1

さて、あなたは(代わりにORの)UNIONのような事を試みることができる - しかし私は、オプティマイザはすでにそれが入手可能な情報与えることができる最高のやっていることをを期待:中

SELECT * FROM Properties 
WHERE NOT EXISTS (SELECT 1 FROM Areas WHERE Areas.AreaID = Properties.AreaID) 
UNION 
SELECT * FROM Properties 
WHERE NOT EXISTS (SELECT 1 FROM Regions WHERE Regions.RegionID = Properties.RegionID) 
UNION 
SELECT * FROM Properties 
WHERE NOT EXISTS (SELECT 1 FROM Countries WHERE Countries.CountryID = Properties.CountryID) 
0

サブクエリを条件が非常に非効率的である可能性があります。代わりに、関連するテーブルに対して左の結合を行うことができます。一致するレコードがない場合は、null値が返されます。この条件でこれを使用して、一致するレコードがないレコードのみを選択することができます。

select p.* 
from Properties p 
left join Countries c on c.CountryID = p.Country_ID 
left join Regions r on r.RegionID = p.RegionID 
left join Areas a on a.AreaID = p.AreaID 
where c.CountryID is null or r.RegionID is null or a.AreaID is null 
0

あなたは国/地域/エリアから行データをつかんでいない場合は、「存在」を使用して試すことができます:

SELECT Properties.* 
FROM Properties 
WHERE Properties.CountryID IS NOT NULL AND NOT EXISTS (SELECT 1 FROM Countries WHERE Countries.CountryID = Properties.CountryID) 
OR Properties.RegionID IS NOT NULL AND NOT EXISTS (SELECT 1 FROM Regions WHERE Regions.RegionID = Properties.RegionID) 
OR Properties.AreaID IS NOT NULL AND NOT EXISTS (SELECT 1 FROM Areas WHERE Areas.AreaID = Properties.AreaID) 

これは通常、国のPKEYインデックスを使用するヒントらう存在チェックのためのものですが、それが改善かどうかは、データ統計に依存します。クエリアナライザにプラグインして試してみるだけです。