個々のパラメーターまたはBuild Where句
-
03-07-2019 - |
質問
データベースで注文を検索するオブジェクトを作成しています。ユーザーが設定できる多数のパラメータがあり、各検索に必要な数だけ設定できます。検索に必要なすべてのパラメーターを収集するセッターメソッドを作成しました。
私の質問はこれです。 「ベストプラクティス」とは何ですか
- パラメータを保存し、
doSearch
メソッドが呼び出されたときにWHERE
句を構築する - パラメータが設定されている場合の
WHERE
句の構築
推奨事項の背後にある理由を理解したい。
オブジェクトは検索ごとにインスタンス化されることに注意してください。したがって、異なるパラメーターを使用した2回目の検索について心配する必要はありません。
解決
注文検索用のコードと、SQLを構築するコードを分離する必要があります。 SQLは、 OrderSearch <の派生物(または Strategy 派生物)で構築する必要があります。 / code>クラス。この分離を行ったら、SQLをいつ 構築するかは重要ではありません。
これをもう少しわかりやすくするために。検索条件用の一連のセッターメソッドを持つ OrderSearch
という名前のクラスを指定すると、 OrderSearchSQLBuilder
という名前のサブクラスが必要になります。サブクラスは基本クラスに依存し、基本クラスはサブクラスから独立していることに注意してください。これはとても重要です。この独立性により、SQLがセッターメソッドで構築されているか、検索メソッドで構築されているかを無視できます。 依存関係逆転原理(DIP)を参照してください。
この種の分離があると、デリバティブを他の戦略に置き換えることができます。たとえば、SQLデータベースに接続せずにアプリケーションをテストする場合、ダミーのインラムデータベースを作成し、そのダミーデータベースを処理する OrderSearch
の派生物を作成できます。アプリケーションの残りの部分はまったく気づかず、テストはデータベース接続、既存のデータなどの恐怖から独立します。
他のヒント
メソッドでは、検索を行う動的SQLのパラメーターを使用するだけです。これにより、SQLが実行される直前にwhere句が構築されます。検索パラメーターを引数としてメソッドに渡すだけです。
このようなもの...
<cffunction name="getByAttributesQuery" access="public" output="false" returntype="query">
<cfargument name="id" type="numeric" required="false" />
<cfargument name="userName" type="string" required="false" />
<cfargument name="firstName" type="string" required="false" />
<cfargument name="lastName" type="string" required="false" />
<cfargument name="createdAt" type="date" required="false" />
<cfargument name="updatedAt" type="date" required="false" />
<cfargument name="orderby" type="string" required="false" />
<cfset var qList = "" />
<cfquery name="qList" datasource="#variables.dsn#">
SELECT
id,
userName,
firstName,
lastName,
createdAt,
updatedAt
FROM users
WHERE 0=0
<cfif structKeyExists(arguments,"id") and len(arguments.id)>
AND id = <cfqueryparam value="#arguments.id#" CFSQLType="cf_sql_integer" />
</cfif>
<cfif structKeyExists(arguments,"userName") and len(arguments.userName)>
AND userName = <cfqueryparam value="#arguments.userName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"firstName") and len(arguments.firstName)>
AND firstName = <cfqueryparam value="#arguments.firstName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"lastName") and len(arguments.lastName)>
AND lastName = <cfqueryparam value="#arguments.lastName#" CFSQLType="cf_sql_varchar" />
</cfif>
<cfif structKeyExists(arguments,"createdAt") and len(arguments.createdAt)>
AND createdAt = <cfqueryparam value="#arguments.createdAt#" CFSQLType="cf_sql_timestamp" />
</cfif>
<cfif structKeyExists(arguments,"updatedAt") and len(arguments.updatedAt)>
AND updatedAt = <cfqueryparam value="#arguments.updatedAt#" CFSQLType="cf_sql_timestamp" />
</cfif>
<cfif structKeyExists(arguments, "orderby") and len(arguments.orderBy)>
ORDER BY #arguments.orderby#
</cfif>
</cfquery>
<cfreturn qList />
</cffunction>
検索の実行に必要になる前にwhere句を作成しないでください。繰り返しでパラメーターを入力するユーザーインターフェイスになる可能性がありますが、すべてが揃っているかどうかはわかりません。また、検索を実行しないこともあるため、なぜwhere句を心配する必要があります。
それほど大きな違いはないと思いますが、doSearchを実行するときはWHERE句を作成する方が良いと思われます。どこかでWHERE句の文字列にパラメータを追加するのは、セッターの責任ではないと思います。
SQLServerデータベースでは、すべてのパラメータをオンザフライで構築するよりも、where句に含める方が効率的です。次に、検索するすべての列を含むデータベースインデックスを作成します。これにより、ステートメントの実行時に常にインデックスが使用されるようになります。
抽象化が正しいとは思えません。
検索は、「注文」の方法としてより良いかもしれません。オブジェクト。パラメータを検索関数に渡し、 russが示唆するようにクエリを手動で作成します。検索ではなく、注文に固有のものはすべて、注文の初期化メソッドで設定できます。
注文検索オブジェクトを作成することもできますが、これは注文オブジェクトを介して行う必要があります。これにより、フロントエンドコードをシンプルに保つことができます。
オプション1が最善の策です。オプション2は危険です。パラメータが更新された場合はどうなりますか? WHERE句でどのように置き換えますか?
このようなことをします:
<cffunction name="doSearch" access="public" output="false" returntype="query">
<cfset var qList = "" />
<cfquery name="qList" datasource="#variables.dsn#">
SELECT
...
FROM
...
WHERE 0=0
<cfif len(getID())>
AND id = <cfqueryparam value="#getID()#" CFSQLType="cf_sql_integer" />
</cfif>
<cfif len(getUserName())>
AND userName = <cfqueryparam value="#getUserName()#" CFSQLType="cf_sql_varchar" />
</cfif>
</cfquery>
<cfreturn qList />
</cffunction>