3

私は次のSQLを持っています:

CREATE TABLE tbFoo(
    a varchar(50) NULL,
) 


CREATE NONCLUSTERED INDEX IX_tbFoo_a ON tbFoo
(
    a ASC
)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)

insert into tbFoo select null
insert into tbFoo select 'test'

次の 2 つのクエリは正常に機能し、期待どおりにインデックスを使用します。

select * from tbFoo where a='test'
select * from tbFoo where a is null

さて、次のように、比較値を変数に格納したいとしましょう。

declare @a varchar(50)
select @a = NULL

「=」ではなく「is」演算子を使用する必要があるため、@a が null の場合、次のクエリは期待される結果を返しません。

select * from tbFoo where a=@a 

以下は機能しますが、 @a が null の場合はテーブル スキャンを実行します (2 番目の括弧の評価を強制する 'test' 行のため)。

select * from tbFoo where (a is null and @a is null) or (a=@a)

最終的に、私はこの解決策を思いつきました。これはうまく機能し、私のインデックスを使用します:

select * from tbFoo where (a is null and @a is null) or (@a is not null and a=@a)

私の状況分析は正しいですか?

この状況を処理するより良い方法はありますか?

4

8 に答える 8

3

最終的に、私はこの解決策を思いつきました。これはうまく機能し、私のインデックスを使用します:

SQL Server 2008 では、NULL を除外する述語に基づいてフィルター選択されたインデックスを定義できます。

CREATE UNIQUE NONCLUSTERED INDEX IX_tbFoo_a 
ON tbFoo (a)
WHERE a IS NOT NULL;
于 2009-06-16T17:10:18.480 に答える
1

おそらくあなたのデータベースエンジンはあなたが持っているものを自動的に最適化します、しかし私には以下がより効率的であるように思われます:

if @a IS NULL
    select * from tbFoo where a is null
else
    select * from tbFoo where a = @a

これについての私の理由はif @a IS NULL、データベース内のすべての行に対して条件をチェックするのではなく、条件を1回だけ実行するということです。繰り返しになりますが、高品質のデータベースエンジンは、コードをこれと同じ種類のデータプランに変換できるはずです。

于 2009-06-16T22:00:13.647 に答える
1

もう 1 つの可能性は、ansi null をオフに設定することです。

set ansi_nulls off

declare @a varchar(50)
select @a = NULL

select * from tbFoo where a=@a

set ansi_nulls on

ここでデフォルトの動作から離れていることに注意してください

于 2009-06-16T18:05:38.390 に答える
1

NULLと「等しい」ものはありません...これは、NULLのポイントのようなものです。

あなたのソリューションはうまくいきます。クエリ オプティマイザーが短いバージョンをどのように処理するかに驚いています。テーブルスキャンで等価性をテストする前に NULL をテストするのは簡単だと思います。

于 2009-06-16T17:05:49.640 に答える
0

ちょうどそのように両側がISNULLです...

DECLARE @random VARCHAR(50)
SELECT  @random = 'text that never appears in your table'

SELECT * FROM @tbFoo WHERE ISNULL(a, @random) = ISNULL(@a, @random)
于 2009-06-16T21:46:02.347 に答える
0

自宅で遊ぶインスタンスはありませんが、テーブル スキャンが非常に煩わしいことがわかります。考えられる代替手段は、OR 演算子の代わりに UNION を使用することです...

select * from tbFoo where (a is null and @a is null)
UNION ALL
select * from tbFoo where (a=@a and @a is not null)

(「@a is not null」がパフォーマンスにどのような影響を与えるかは正確にはわかりませんが、私の直感ではそれを含めることになります。条件全体がいつ失敗するかをオプティマイザーが認識できるようにする定数式です。私のテクニックは、常にプレイして、何が最も効果的かを確認することです。)

この UNION トリックには 2 つのプロパティがあることがわかりました。 -
クエリを単純化することでパフォーマンスを大幅に向上させることができます。

しかし、人生はバランスをとる行為です:)

于 2009-06-16T21:33:57.677 に答える
0

あなたの分析は正しいです。それが、3 値論理が困難な理由です。

@StriplingWarrior からの提案は良いです。変数がnullかどうかに応じて異なるSQLを実行することで、問題を解決します。それが不可能な場合は、ホスト変数を繰り返し使用する長い解決策が必要です。

于 2009-06-16T22:10:33.423 に答える
0

これが私がすることです。とても柔軟です。@a が sproc の引数であると想定しています。'somethingweird' は、レコードセット '~~~' などで決して見られないものになる可能性があります。

set @a = isnull(@a,'somethingweird')
select * from tbFoo where isnull(a,'somethingweird')=@a
于 2009-06-16T17:15:10.910 に答える