7

私は3つのテーブルを持っています:

SmallTable
   (id int, flag1 bit, flag2 bit)
JoinTable
   (SmallTableID int, BigTableID int)
BigTable
   (id int, text1 nvarchar(100), otherstuff...)

SmallTableには、せいぜい数十件のレコードしかありません。BigTable数百万あり、実際には、このデータベースのテーブルを同じサーバー上の別のデータベースのテーブルと UNIONS するビューです。

結合ロジックは次のとおりです。

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=1 OR b.text1 <> 'value1')

平均結合サイズは数千の結果です。表示されるものはすべて索引付けされています。

ほとんどのSmallTableレコードでは、flag1flag2が に設定されて1いるため、BigTable.text1 のインデックスにアクセスする必要さえありませんが、SQL Server はアクセスするため、コストのかかるインデックス スキャンとネスト ループが発生します。

との両方が に設定されている場合flag1、を気にする必要がないことを SQL Server に示唆するより良い方法はありますか?flag21text1

実際、これらの場合に BigTable への結合を完全に回避できれば (JoinTable は管理されているため、問題は発生しません)、このキー クエリはさらに高速になります。

4

6 に答える 6

5

SQLブール評価は、演算子の短絡を保証するものではありません。演算子の短絡を想定すると、正当性の問題や実行時エラーが発生する可能性があることを示す明確な例については、SQLServerのブール演算子の短絡についてを参照してください。

一方、私のリンクの例は、SQL Serverで機能するもの、つまりSQLが使用できるアクセスパスを提供することを示しています。したがって、すべてのSQLパフォーマンスの問題や質問と同様に、実際の問題はSQLテキストの表現方法ではなく、ストレージの設計にあります。つまり。クエリを満たすためにクエリオプティマイザを自由に使用できるインデックスはどれですか?

于 2010-01-25T21:24:39.963 に答える
1

残念ながら、SQL Server がそのような状況を回避するとは思えません。

だから私は2つのクエリを実行し、それらを一緒にUNIONすることをお勧めします。s.flag1=1 および s.flag2=1 WHERE 条件を使用した最初のクエリと、s.flag1<>1 a s.flag2<>1 条件を使用して BigTable への結合を行う 2 番目のクエリ。

この問題に関するこの記事は一読の価値があり、要点が含まれています。

...SQL Server は、他のプログラミング言語で行われているような短絡を行わず、強制するためにできることは何もありません。

更新:
この記事は興味深い読み物でもあり、オプティマイザーが短絡評価を許可することについて簡単に言及している SQL Server クエリ プロセッサ チームの開発マネージャーとのテクネット チャットなど、このトピックに関するいくつかの優れたリンクが含まれています。さまざまな記事から得た全体的な印象は、「はい、オプティマイザーは短絡の機会を見つけることができますが、それに頼るべきではなく、強制することはできません」です。したがって、UNIONアプローチが最善の策であると思います。近道をする機会を利用する計画を考え出していない場合、それはコストベースのオプティマイザが、それを行わない合理的な計画を見つけたと考えていることに起因します (これは、インデックス、統計などに起因します)。 .

于 2010-01-25T21:17:46.067 に答える
0

SQL Serverは通常、サブクエリのヒントを取得します(ただし、それは自由に破棄できます)。

SELECT      * 
FROM        (
            SELECT * FROM SmallTable where flag1 <> 1 or flag2 <> 1
            ) s
INNER JOIN  JoinTable j ON j.SmallTableID = s.ID
...
于 2010-01-25T21:24:41.420 に答える
0

これがテストデータなしでより速くなるかどうかはわかりません...しかし、そうなるかもしれません

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1) AND (s.flag2=1)
 UNION ALL
 SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=0 AND b.text1 <> 'value1')

何が起こるか教えてください

また、このクエリの一意の ID だけを返し、その結果を使用して残りのすべてのデータを取得することで、これを高速化できる場合があります。

編集

このようなもの?

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1) AND (s.flag2=1)
 UNION ALL
 SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE EXISTS
    (SELECT 1 from BigTable b
     WHERE   
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=0 AND b.text1 <> 'value1')
)
于 2010-01-25T21:15:56.957 に答える
0

エレガントではありませんが、うまくいくはずです...

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1 = 1 and s.flag2 = 1) OR 
    (
       (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
       AND (s.flag2=1 OR b.text1 <> 'value1')
    )
于 2010-01-25T21:16:09.677 に答える
0

caseこれが機能することを願っています-ステートメントのショートカットロジックに注意してくださいaggregates...

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE 1=case when (s.flag1 = 1 and s.flag2 = 1) then 1
when (
       (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
       AND (s.flag2=1 OR b.text1 <> 'value1')
    ) then 1
else 0 end

于 2019-06-14T12:10:39.920 に答える