10

SQL Server の定数の欠如を「克服」するために使用されるいくつかのパターンを見てきましたが、パフォーマンスと可読性/保守性の両方の懸念を満たすものはないようです。

以下の例では、テーブルに不可欠な「ステータス」分類があると仮定すると、オプションは次のようになります。

  • ハードコーディングするだけで、おそらくステータスを「コメント」するだけです

-- StatusId 87 = Loaded
SELECT ... FROM [Table] WHERE StatusId = 87;
  • 州のルックアップ テーブルを使用し、このテーブルに結合して、WHERE句がフレンドリ名を参照するようにします。

サブクエリ:

SELECT ... 
FROM [Table] 
WHERE 
  StatusId = (SELECT StatusId FROM TableStatus WHERE StatusName = 'Loaded');

または加入

SELECT ... 
FROM [Table] t INNER JOIN TableStatus ts On t.StatusId = ts.StatusId 
WHERE ts.StatusName = 'Loaded';
  • 定数を返す一連のスカラー UDF が定義されています。

CREATE Function LoadedStatus()
RETURNS INT
AS
 BEGIN
  RETURN 87
 END;

その後

SELECT ... FROM [Table] WHERE StatusId = LoadedStatus();

(IMOこれはデータベースに多くの汚染を引き起こします-これはOracleパッケージラッパーでは問題ないかもしれません)

  • また、値を持つ定数を行または列として保持するテーブル値関数を使用した同様のパターンはCROSS APPLIED[Table]

他の SO ユーザーは、この一般的な問題をどのように解決しましたか?

編集:報奨金 - Remus の回答とコメントに従って、DBProj DDL / Schema スクリプトで $(variables) を維持するためのベスト プラクティスの方法はありますか?

4

4 に答える 4

15

ハードコーディング。SQL パフォーマンスは保守性よりも優先されます。

プランの生成時にオプティマイザーが検査できる定数を使用する場合と、任意の形式の間接参照 (UDF、JOIN、サブクエリ) を使用する場合の実行プランの結果は、しばしば劇的です。SQL の「コンパイル」は、結果がコンパイルされる言語構造 (つまり、クエリの実際のテキスト) だけでなく、データ スキーマ (既存のインデックス)それらのインデックス内の実際のデータ (統計) によって。ハードコーディングされた値が使用されると、オプティマイザは実際にインデックス統計に対して値をチェックし、結果の見積もりを取得できるため、より適切な計画を立てることができます。

もう 1 つの考慮事項は、SQL アプリケーションはコードだけではなく、大部分がコードデータであるということです。SQLプログラムの「リファクタリング」は...違います。C# プログラムでは、定数または列挙型を変更し、アプリケーションを再コンパイルして問題なく実行できますが、SQL では、データベース内の何百万ものレコードに値が存在する可能性が高く、定数値を変更すると GB 単位のデータも変更されるため、そうすることができません。 、新しい操作が発生している間、多くの場合オンラインです。

サーバーが参照するクエリとプロシージャで値がハードコードされているからといって、元のプロジェクト ソース コードで値をハードコードする必要があるとは限りません。これを処理できるさまざまなコード生成ツールがあります。sqlcmd スクリプト変数を活用するのと同じくらい簡単なことを考えてみましょう。

defines.sql:

:setvar STATUS_LOADED 87

somesource.sql:

:r defines.sql
SELECT ... FROM [Table] WHERE StatusId = $(STATUS_LOADED);

someothersource.sql:

:r defines.sql
UPDATE [Table] SET StatusId = $(STATUS_LOADED) WHERE ...;
于 2010-07-30T12:20:35.223 に答える
6

IMOのRemus Rusanuに同意しますが、コードの保守性(したがって、読みやすさ、驚きの少なさなど)は、パフォーマンスの違いが他の方法を正当化するのに十分なほど重要でない限り、他の懸念より優先されます。したがって、次のクエリは可読性を失います。

Select ..
From Table
Where StatusId = 87

一般に、コードで参照されるシステム依存の値がある場合 (名前による列挙で模倣される可能性があります)、それらが保持されるテーブルに文字列の主キーを使用します。これを、私が通常代理キーを使用するユーザー変更可能なデータと比較してください。入力が必要な主キーを使用すると、(完全ではありませんが) 他の開発者に対して、この値が恣意的なものではないことを示すのに役立ちます。

したがって、私の「ステータス」テーブルは次のようになります。

Create Table Status
(
    Code varchar(6) Not Null Primary Key
    , ...
)
Select ...
From Table
Where StatusCode = 'Loaded'

これにより、クエリが読みやすくなり、Status テーブルへの結合が不要になり、マジック ナンバー (または GUID) を使用する必要がなくなります。ユーザー定義関数を使用する場合、IMO は悪い習慣です。パフォーマンスへの影響を超えて、UDF がこのように使用されることを期待する開発者はいないため、驚きの少ない基準に違反します。各定数値に対して UDF を持たなければならないことはほとんどありません。それ以外の場合、関数に渡すもの: 名前? 魔法値?名前の場合は、その名前をテーブルに保持し、クエリで直接使用することもできます。魔法の値の場合、元の問題に戻ります。

于 2010-08-06T16:36:56.907 に答える
3

私はDBでスカラー関数オプションを使用していますが、それはうまく機能しており、私の意見では、このソリューションの最良の方法です.

1 つの項目に関連する値が複数ある場合は、コンボボックスやその他の静的な値を持つコントロールをロードする場合のように検索を行い、検索を使用するのが最善の方法です。

于 2010-07-30T11:19:09.410 に答える
2

ステータス値の一意のマーカーまたはグルーパーとして機能するフィールドをステータス テーブルに追加することもできます。たとえば、ステータス テーブルに isLoaded フィールドを追加すると、フィールドの値が設定されているのはレコード 87 だけである可能性があり、ハードコーディングされた 87 またはステータスの説明の代わりに、isLoaded フィールドの値をテストできます。

于 2010-08-05T17:42:40.420 に答える