web-dev-qa-db-ja.com

このWHERE句ビルダーは過度に設計されたデザインですか?

私が取り組んでいるプロジェクトでは、SQLでやや複雑なWHERE句を作成する必要があります。これらの句は、ANDとORの組み合わせで非常に階層的に感じられます。の代わりに:

WHERE ([userId] NOT IN @excludeUsers) AND ((([firstname] LIKE @nameFilter) OR ([surname] LIKE @nameFilter)) AND (([jobTitle] LIKE @infoFilter) OR ([mobileNo] LIKE @infoFilter)))

...次のようなものを書けるようにしたい:

// Wcb is a WhereClauseBuilder
OrClause innerOr;
var whereClause =
    Wcb.And(
        "[userId] NOT IN @excludeUsers",
        Wcb.And(
            Wcb.Or(
                "[firstname] LIKE @nameFilter",
                "[surname] LIKE @nameFilter"
            ),
            innerOr = Wcb.Or(
                "[jobTitle] LIKE @infoFilter",
                "[mobileNo] LIKE @infoFilter"
            )
        )
    );

アイデアは、空白、角括弧、AND/ORキーワードの欠落などの間違いをクエリから排除することです。 AndおよびOr静的メソッドはAndClauseおよびOrClauseクラスのインスタンスを作成し、ToStringをオーバーロードしてオブジェクト全体を許可します$"{whereClause}"で文字列に解決するグラフ。次のように、後でクエリに追加できるようにもしたいと思います。

if (extraInfoFilter != null) {
    innerOr.Or(
        "[extraInfo] LIKE @extraInfoFilter"
    );
}

しかし、私がこれのために書いているコードは、尋ねるように要求するほど複雑になっています:このソリューションは過度に設計されていますか?このような階層オブジェクトモデルから文字列を生成するのではなく、文字列を手動で作成する必要がありますか?それがより良いアプローチになる理由があるでしょうか?

4
Jez

クエリのコンポーネントがすべて事前にわかっている場合、これは過剰に設計されます。いくつかの動的基準を必要とする1つのクエリがある場合は、おそらく文字列の連結を行い、それで完了します。動的条件がいくつかある場合、または動的条件を必要とする複数のクエリがある場合、他のユーティリティがない限り、クエリビルダーオブジェクトに時間を費やすことは間違いなく正当化されます。

10
Greg Burghardt

あなたの提案は過度に設計されているようです。 SQLを適切にフォーマットするだけで、条件は、提案した代替案と少なくとも同じくらい理解しやすくなります。

WHERE
([userId] NOT IN @excludeUsers) 
AND 
(
    (
        ([firstname] LIKE @nameFilter) 
        OR 
        ([surname] LIKE @nameFilter)
    ) 
    AND
    (
        ([jobTitle] LIKE @infoFilter)
        OR
        ([mobileNo] LIKE @infoFilter)
    )
)
9
Steve

この全体の問題は、SQLをホスト言語(C#)の文字列リテラルで受け入れるために発生します。 SQLステートメントを効果的にフォーマットした人は、すべてのC#を便利に取り除きましたが、どのようにしてクエリを実行しますか?

これに対する私の答えは、QueryFirst、ビジュアルスタジオ拡張です。構文チェックとインテリセンスを備えた実際の環境でSQLを記述します。その後、保存するたびに、クエリを呼び出すことができるC#ラッパーが生成されます。アプリを実行しなくても、クエリはDBに対して継続的に検証されます。 DRY-列名を繰り返したり、データ型を調べたり、結果フィールドをオブジェクトにマップしたりする必要はありません。SQLインジェクションは不可能になります。他の方法よりも簡単です。仕方。

0
bbsimonbb

フォーマット

適切なフォーマットは非常に役立ちます。余分な括弧を削除し、ネストすることでクエリの解析が容易になるためです。

あなたのクエリはただです:

WHERE
    [userId] NOT IN @excludeUsers
    AND 
    ([firstname] LIKE @nameFilter OR [surname] LIKE @nameFilter)
    AND
    ([jobTitle] LIKE @infoFilter OR [mobileNo] LIKE @infoFilter)

これは必ずしも最小限ではありません。 AND/OR演算子の相対的な優先順位を手元で覚えていないため、通常はメモリに依存して(おそらく失敗する)括弧を使用して優先順位を付けます。

注:複数行の文字列リテラルを使用する言語では適切なフォーマットが簡単であるという言及がありましたが、文字列の連結をサポートする言語はどれも実際に機能します。

クエリビルダー

クエリビルダーには、生のクエリよりも大きな利点があります。重要な利点の1つは、コンパイル時にスペルミスがないことを確認することです。つまり、joTitleinfoFllterの両方をキャッチします。

ただし、これには、ここにあるものよりもはるかに複雑なクエリビルダーが必要です。

  • SQLモデルを言語構造としてアプリケーションに埋め込む必要があります。
  • バインディングを表現する方法が必要です。

一方、SQL言語がかなり複雑であるため、クエリビルダーがすぐに急上昇する傾向があるという問題があります。また、ユーザーがWITHまたはJOINの使用を開始すると、SQLモデルの操作が突然(効率的に)非常に複雑になります。

0
Matthieu M.