使用OR或ColdFusion过滤SQL查询Where子句?

时间:2018-08-09 21:08:11

标签: sql sql-server-2008 coldfusion where-clause coldfusion-2016

我有一个搜索表单,用户可以在其中向服务器提交请求之前选择一些条件。这涉及可以由NameNumberShow 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查询上使用它。阅读更多并进行研究似乎是一个非常坏的习惯,不应使用。我正在构建新系统,而我所寻找的只是针对这种情况的良好实践,这种情况不会导致效率低下的系统和难以维护的代码。如果有人有类似问题的经验,请告诉我您如何处理。我不确定现在采用哪种方法以及什么是最佳实践。

1 个答案:

答案 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>