WHERE句を更新して、値が別のテーブルにないかどうかを確認する


8

私が使用するクエリ持っているWHERE句を、と私は起こるまったく同じ使用することWHEREで句多く、このテーブル上のクエリ(ら)。

クエリは次のとおりです。

SELECT
    DATENAME(DW, [AtDateTime]) AS [Day of Week]
    ,COUNT(*) AS [Number of Searches]
    ,CAST(CAST(COUNT(*) AS DECIMAL(10, 2)) 
         / COUNT(DISTINCT CONVERT(DATE, [AtDateTime])) AS DECIMAL(10, 2)) 
       AS [Average Searches per Day]
    ,SUM(CASE WHEN [NumFound] = 0 THEN 1 ELSE 0 END) 
       AS [Number of Searches with no Results]
    ,CAST(CAST(SUM(CASE WHEN [NumFound] = 0 THEN 1 ELSE 0 END) 
         AS DECIMAL(10, 2)) / COUNT(*) AS DECIMAL(10, 4)) 
       AS [Percent of Searches with no Results]
FROM [DB].[dbo].[SearchHistory] 
WHERE 
    [CustomerNumber] <> '1234' AND [CustomerNumber] <> '5678'
GROUP BY DATENAME(DW, [AtDateTime]), DATEPART(DW, [AtDateTime])
ORDER BY DATEPART(DW, [AtDateTime])

変更したい部分は、WHERE代わりにテーブルを使用できるようにする句です。無視する顧客番号を追加する必要がある場合、すべてのクエリを更新する必要はありません。(そして、これと同じWHERE句を持つクエリはかなりたくさんあります。)


顧客の除外が現在クエリ実行に固有のものである場合、それらを共有テーブル/ワークテーブルに移動しても、誤った共有が導入されないのはなぜですか?通常のアプリケーションでは、顧客は通常任意であり、単一のクエリ実行に固有です。この質問は、ソリューションが正しく機能するために必要な一般性に関する重要な事実を省略している、または共有の問題を見落としていることをお勧めします。
トーマスW

@ThomasW-あなたが話しているこの「偽りの共有」とは何ですか?そのためのリファレンスはありますか?今まで聞いたことがありません。
Max Vernon

1
@ThomasWこれに対する要件は、私たちが持っている特定の顧客(私たちはテストに多く使用しています)が特定のレポートから除外される必要があることです。
Der Kommissar

1
@MaxVernon-おそらくより適切に認識される用語は「範囲が正しくない」でしょう。説明されたことは、完全に独立したパラメーターから、クロスユーザー、クロス呼び出しの共有DBテーブルになるように入力を変更することを含みました。この変更は、2つのスコープ境界を超えています。追加のコンテキストが与えられた場合、説明されたスコープは問題ないように見えますが、そうでない場合、これは「誤った共有」として現れます。
トーマスW

1
説明されているアプローチは、私が担当している主要なアプリケーションにおけるレガシーの作業テーブルの実装全体(〜1000テーブル)を連想させるものでもありました。この点で、私は「ワークテーブル」の可能性を質問として挙げました:)ありがとう。
トーマスW

回答:


5

除外する顧客番号を保持するテーブルを作成NOT EXISTSし、WHERE句でa を使用してそれらの行を除外します。

CREATE TABLE dbo.ExcludedCustomers
(
    CustomerNumber VARCHAR(255) NOT NULL
        CONSTRAINT PK_ExcludedCustomers
        PRIMARY KEY CLUSTERED
);

INSERT INTO dbo.ExcludedCustomers (CustomerNumber)
VALUES ('1234')
    , ('5678');


SELECT
    <....>
FROM [DB].[dbo].[SearchHistory] 
WHERE 
    NOT EXISTS (
        SELECT 1
        FROM dbo.ExcludedCustomers ec
        WHERE ec.CustomerNumber = SearchHistory.CustomerNumber
    )
    <...>;

7
CREATE TABLE dbo.CustomerExclusions
(
  CustomerNumber VARCHAR(32) PRIMARY KEY -- Is CustomerNumber *really* a string?
);

INSERT dbo.CustomerExclusions(CustomerNumber) VALUES('1234'),('5678');

これで、WHEREすべてのクエリの句は次のようになります。

WHERE NOT EXISTS 
(
  SELECT 1 FROM dbo.CustomerExclusions AS c
  WHERE c.CustomerNumber = SearchHistory.CustomerNumber
)

はい、残念ながら。顧客番号、AS / 400との互換性のために文字列でなければなりません。(少なくとも今のところ、その修正に取り組んでいます。)
Der Kommissar

3
@EBrownええと、うーん。
アーロンバートランド

-3

提案されたアプローチには、重要な質問/潜在的な問題があります。確かに、「顧客番号の除外」作業テーブルを使用して簡単に除外できます。

WHERE NOT EXISTS (
  SELECT 1 FROM [dbo].Work_ExcludeCustomer
  WHERE CustomerNumber = SearchHistory.CustomerNumber
)

しかし今、「クエリパラメータ」とは何か-完全に動的で独立した、クエリごとおよびユーザーごと-は、「データベースの共有永続状態」に変わりつつあります。

いくつかの質問と関連ポイント:

  1. 顧客の除外情報は、ユーザーごとまたはセッションごとに個別にする必要がありますか?これらを区別するために 'SessionID'パラメータを追加できますが、基本的には古い古い「ワークテーブル」パターンを再作成しています。

  2. たぶん、NOT IN(...)句が望ましいでしょうか?動的にパラメーター化できます。パラメーターの上限は2100です。

  3. 現在、固定パラメータ番号に依存している場合は、コード/インフラストラクチャに再度アクセスして、クエリを構築し、パラメータをバインドします。これを改善すると、モジュール化が可能になり、可変数のパラメーターを持つINまたはNOT IN(?、?、?..)節の使用が可能になります。

推奨されるアプローチ:

WHERE [CustomerNumber] NOT IN (?, ?, ?)

NOT IN()パラメータへのバインディング「1234」、「5678」、「6789」などと、適切な番号付けに動的にバインドされた後続の論理クエリパラメータ。


1
NOT IN(...)の使用や動的にクエリテキストを作成することはアンチパターンであり、Aaronや私が推奨するセットベースのアプローチよりもパフォーマンスが低下します。
Max Vernon

違いについての優れた資料については、この投稿をチェックしてください。
Max Vernon

@MaxVernon-動的パラメーターを「共有」データまたは作業テーブルで置き換えると、誤った共有が導入される可能性があり、これはアンチパターンのはるかに多くなります。これが問題ではないと特に考えたり確立したりした人は他にいないので、この懸念を提起することは絶対に有効です。また、当然のことながら反対投票もしないでください。
トーマスW
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.