我有一个搜索表单,用户可以在其中向服务器提交请求之前选择一些条件。这涉及可以由Name
,Number
或Show All
搜索的搜索条件。在我们现有的(旧系统)中,以前的程序员使用了以下内容:
<cfquery name="qryFindRecord" datasource="#dsn#">
SELECT RecID, Number, Name
FROM Dictionary WITH (NOLOCK)
WHERE 1 = 1
AND
<cfswitch expression="#arguments.frm_filterby#">
<cfcase value="1"><!--- Name --->
Name LIKE <cfqueryparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50" />
</cfcase>
<cfcase value="2"><!--- Number --->
Number = <cfqueryparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2" />
</cfcase>
<cfdefaultcase><!--- Show All --->
1 = 1
</cfdefaultcase>
</cfswitch>
ORDER BY Name
</cfquery>
如您所见,他们使用switch
语句来评估参数filter by
并基于该搜索查询。我当时认为仅涉及SQL的解决方案在维护和效率方面会更好。这是示例:
<cfquery name="qryFindRecord" datasource="#dsn#">
DECLARE @FilterBy INT = <cfqueryparam value="#trim(arguments.frm_filterby)#" cfsqltype="cf_sql_integer" />;
SELECT RecID, Number, Name
FROM Dictionary
WHERE
(@FilterBy = 1 AND Name LIKE <cfqueryparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50" />)
OR
(@FilterBy = 2 AND Number = <cfqueryparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2" />)
ORDER BY Name
</cfquery>
第一个选项(当前系统中使用的旧方法)似乎不适合与SQL一起使用,但我不是100%,因为我对SQL不了解。第二个原因是WITH (NOLOCK)
,高级程序员告诉我,我们应该在系统中的每个SELECT查询上使用它。阅读更多并进行研究似乎是一个非常坏的习惯,不应使用。我正在构建新系统,而我所寻找的只是针对这种情况的良好实践,这种情况不会导致效率低下的系统和难以维护的代码。如果有人有类似问题的经验,请告诉我您如何处理。我不确定现在采用哪种方法以及什么是最佳实践。
答案 0 :(得分:2)
我对MS SQL Server很熟悉,所以对YMMV很熟悉。
WITH(NOLOCK)
应视情况决定。当正确性不如性能重要时,这才有意义。它还很大程度上取决于数据库中的操作类型。
关于您的cfquery
,我认为您的重构很好。任何看起来更接近存储过程的东西都更有可能被RDBMS参数化,这意味着高效的执行计划缓存。如果可以的话,我会把它变成一个存储过程。
由于这是一个简单的查询,请考虑将条件逻辑提升到ColdFusion中,并使用两个查询或存储过程。
您执行的动态SQL越少,暴露SQL Injection缺陷的风险就越小。我还发现,经过多年调试使用大量动态SQL的ColdFusion应用程序之后,每当看到1 = 1
时,我都会抽搐。通常这是您应该开始重构的信号。
例如,为了通过从查询中提升条件逻辑来进一步简化查询,您可以说:
<cfif arguments.frm_filterby eq 1>
<cfquery name="qryFindRecord" datasource="#dsn#">
SELECT RecID, Number, Name
FROM Dictionary
WHERE Name LIKE <cfqueryparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50"/>
ORDER BY Name
</cfquery>
<cfelseif arguments.frm_filterby eq 2>
<cfquery name="qryFindRecord" datasource="#dsn#">
SELECT RecID, Number, Name
FROM Dictionary
WHERE Number = <cfqueryparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2"/>
ORDER BY Name
</cfquery>
<cfelse>
<!--- Invalid request? --->
</cfif>
真正的区别在于您是想主要阅读ColdFusion还是SQL。
或者使用stored procedure隐藏一个数据库内部的版本:
<cfif arguments.frm_filterby eq 1>
<cfstoredproc procedure="DictionaryByName" datasource="#dsn#">
<cfprocparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50"/>
<cfprocresult name="qryFindRecord"/>
</cfstoredproc>
<cfelseif arguments.frm_filterby eq 2>
<cfstoredproc procedure="DictionaryByNumber" datasource="#dsn#">
<cfprocparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2"/>
<cfprocresult name="qryFindRecord"/>
</cfstoredproc>
<cfelse>
<!--- Invalid request? --->
</cfif>