6

クライアントからのカスタム データへのアダプタを作成しています。新しいインデックスを提案することはできますが、スキーマを変更したり、テーブルの値を変更したりすることはできません。このアプローチは、CTE を使用してカスタム データを結合し、再フォーマットして、列名、列挙値などを使用することです。データが再フォーマットされると、標準の CTE を追加して、標準の分析を実行できるクエリをそれから作成できます。

再フォーマットの結果の一部の値は、LEFT JOIN が一致しなかったため、または実際には NULL であるデータの値が原因で NULL になります。

私の仕事は、多くのフィールドで NULL をデフォルト値に置き換え、クエリに WHERE 句を挿入できるようにすることです。現在、デフォルト値の処理には ISNULL 呼び出しまたは CASE ステートメントが使用されています。現在、WHERE 条件がヒットするまでに、この置換は既に実行されているため、クエリ ビルダーにアクセスできるエンド ユーザーは、既定値である可能性のある値をフィルター処理できます。フィルタ値がデフォルト値の場合、デフォルト値に置き換えられた NULL 値を持つレコードを選択する必要があります。

問題は、再フォーマット式として myField = ISNULL(myField, 'MyDefault') があり、後でタマネギの外層 (後の CTE) に WHERE myField = 'MyDefault' がある場合、この where 句はsargable: クエリ オプティマイザは myField のインデックスを選択しません。

私が思いつく部分的な解決策は、内部 CTE で NULL 置換を行わず、WHERE 句を挿入する CTE を作成し、すべての NULL 置換を実行する外部 CTE を作成することです。このようなクエリでは、インデックスを使用できます。(私はこれを確認しました。)しかし、where 句は、デフォルト値に対する値のテストでも NULL 値を持つレコードを取得することを期待できなくなりました。これは、その置換がまだ行われていないためです。

null 置換を実行し、SARGABLE where フィルターを許可し、NULL 値をデフォルト値を保持しているかのようにフィルター処理する方法はありますか?

問題のサイズに関する注意: 典型的な例では、600 万レコードのテーブルを 700 万レコードのテーブルに結合し、1200 万のレコードを作成する多対多のリレーションシップを使用します。フィルターが SARGABLE の場合、クエリには約 10 秒かかります。SARGABLE でない場合、1 台のマシンで 10 分以上、より高速なマシンでは 3 分以上かかります。

選択したソリューションに関するコメント:

フィールドを NULL または非 NULL と ISNULL またはその他のサージ不可関数なしで比較できるようにするための交差の巧妙な使用は、従来のクエリへの変更を最小限に抑えてコードに組み込むことができます。

コメント 2: ケースの欠落

次の 6 つのケースがあります。

  1. 選択された値は null ではなく、デフォルトと等しくなく、フィルタ値と一致しません。除外する必要があります。
  2. 選択された値は null ではなく、デフォルトと等しくなく、フィルター値と一致します。含める必要があります。
  3. 選択された値は null ではなく、デフォルト値と等しく、フィルタ値と一致しません。除外する必要があります。
  4. 選択された値は null ではなく、DOES はデフォルト値と等しく、DOES はフィルター値と一致します。含める必要があります。
  5. 選択された値は null であり、フィルタ値はデフォルトではありません。除外する必要があります。
  6. 選択された値は null で、フィルタ値はデフォルトです。含める必要があります。

ケース 4 は、提供されたソリューションを使用しても機能しません。選択したフィールドは null ではないため、交差部分の前半には非 null 値を持つレコードがあります。しかし、交差点の後半では、NULLIF ステートメントによって null 値を持つレコードが作成されています。交差はゼロ レコードを生成します。レコードは拒否されます。このケースを処理するソリューションをまだ探しています。とても近い...

更新ソリューション:

修正があります。[County Name] に適合していて、デフォルト値が「不明」であるとします...

where EXISTS (
    select [County Name] 
    intersect 
    (select NULLIF('User selected county name', 'Unknown') union select 'User selected county name')
)
4

4 に答える 4

3

既にクエリを動的に作成しているように見えるので、ツールからフィルター処理が必要な値を取得すると、このような where 句を使用してクエリを作成できます。

SQL フィドル

MS SQL Server 2008 スキーマのセットアップ:

create table YourTable
(
  ID int identity primary key,
  Name varchar(20)
)

create index IX_YourTable_Name on YourTable(Name)

insert into YourTable values
('Name1'),
('Name2'),
(null)

クエリ 1 :

declare @Param varchar(20)
set @Param = 'DefaultName'

select ID,
       coalesce(Name, 'DefaultName') as Name
from YourTable
where exists(select Name intersect select nullif(@Param, 'DefaultName'))

結果

| ID |        NAME |
--------------------
|  3 | DefaultName |

クエリ 2 :

declare @Param varchar(20)
set @Param = 'Name1'

select ID,
       coalesce(Name, 'DefaultName') as Name
from YourTable
where exists(select Name intersect select nullif(@Param, 'DefaultName'))

結果

| ID |  NAME |
--------------
|  1 | Name1 |

上記のクエリのクエリ プランは、シークに IX_YourTable_Name を使用します。

ここに画像の説明を入力

参照:文書化されていないクエリ プラン: 等値比較

于 2013-06-21T18:29:12.797 に答える
1

あなたはスキーマを変更できないと言いましたが、私はここで枠の外で考えています。既存のデータベースを調べるビューを持つ新しいデータベースを追加できます。例えば:

use NewViewDb
GO

CREATE VIEW dbo.[T1T2View]
AS
SELECT field1, field2, COALESCE(field3, 'default value'), ...
FROM RealDb.dbo.Table1 t1 LEFT JOIN RealDb.dbo.Table2 t2 
ON t1.Id = t2.Id

GO
于 2013-06-21T18:09:39.550 に答える
0

したがって、最終的に問題は、データを送信するときにISNULL(または) を使用することではなく、フィルタリングにあります。COALESCE問題はOR、述語で句を使用する場合、通常はインデックスを使用できないことです。それが、これらのステートメントのほとんどです。

最終的に、解決策は、SQLサーバー内でこれを行う必要がある場合は動的SQLを使用するか、SQLサーバーの外部にいる場合はエンドクエリを作成することです。ストアド プロシージャ内からこれを実行している場合は、SQL クエリを作成してsp_executesqlから、生成されたクエリを呼び出すことになります。

ここで行うことの重要なポイントは、フィルター処理する値 (つまり、「My Default」) を検査し、それがその値である場合は、値をフィルター処理する述語を追加するか、追加することです。値を制約する述語IS NULL

これがどのように行われるかについてさらに情報が必要な場合は、サンプル クエリを提供できます。

于 2013-06-21T18:17:38.333 に答える
0

CTE の代わりに一時テーブルを使用する場合は、データを入力してからインデックスを作成できます。

于 2013-06-21T17:54:03.023 に答える