在静态SQL中实现动态WHERE子句

目录

介绍

实现动态WHERE-Clause

使用COALESCE

使用ISNULL

使用CASE

替代方案

结论


本文介绍了在静态SQL中实现动态WHERE-Clause的不同方法。

介绍

我之前的文章是关于在存储过程中构建动态SQL。我解释了如何使用sp_executesql命令编写和执行动态SQL。那么,当我们看一下这两种方法的查询执行计划(静态和动态)速度和性能存在巨大差异,因为检查静态SQL是否存在语法错误、已解析、编译并且执行计划存储在SQL Server的缓存中以供后续执行。正如我在上一篇文章中特别提到的那样,变量中的动态SQL查询不会被编译、解析、检查错误,直到它们被执行。SQL Server不可能重用动态SQL语句生成的执行计划。当性能是最高优先级时,应该避免使用动态SQL语句。速度、性能和可重用性是Dynamic SQL的已知问题。在这里,我想展示几种在静态SQL中实现动态WHERE-Clause的方法。我很高兴写这篇文章会让你想到一个更好的方法而不是直接来编写动态SQL。对于那些阅读此内容的人,我建议您查看我之前的文章,以便更好地理解。了解这两种方法的细节总是有利的,这有助于您选择更有效的方法。

实现动态WHERE-Clause

使用COALESCE

SQL Server中的COALESCE函数在处理具有NULL值的列时更有用。它有助于我们采用更有效的方法来构建动态WHERE-clause。在我们进入例子之前,让我解释一下这个COALESCE函数是如何工作的。

基本语法: COALESCE()

COALESCE ( expression1 , expression2, expression3....n)

COALESCE 函数接受多个表达式作为参数,并从左到右处理表达式列表。此函数返回表达式列表中的第一个Non-Null表达式。如果所有参数都是NULL,则返回NULL

注意:所有表达式必须属于同一类型,或者必须可隐式转换为相同类型。

让我们举个例子——带有公共字段的Employee表,并将语句包装在存储过程中。下面的Transact-SQL CREATE TABLE语句是在你的数据库中创建一个Employees表。

/* Transact-Sql to create the table tblEmployees */
CREATE TABLE tblEmployees
(
    EmployeeID       SMALLINT IDENTITY(1001,1) NOT NULL,
    EmployeeName     NVARCHAR(100) NOT NULL,
    Department       NVARCHAR(50) NOT NULL,
    Designation      NVARCHAR(50) NOT NULL,
    JoiningDate      DATETIME NOT NULL,
    Salary           DECIMAL(10,2) NOT NULL
)

以下INSERT语句将一些示例记录插入tblEmployee表中:

/* Transact SQL to insert some sample records into tblEmployees table */
INSERT INTO tblEmployees
(EmployeeName, Department, Designation,  JoiningDate, Salary) VALUES
('John Smith', 'IT Research', 'Research Analyst', '02/08/2005', 23000.00)

INSERT INTO 
tblEmployees(EmployeeName, Department, Designation, JoiningDate, Salary) 
VALUES('John Micheal', 'IT Operations', 'Manager', '07/15/2007', 15000.00)

INSERT INTO 
tblEmployees(EmployeeName, Department, Designation, JoiningDate, Salary) 
VALUES('Will Smith', 'IT Support', 'Manager', '05/20/2006', 13000.00)

下面编写的存储过程(例2.1)可用于在EmployeetblEmployees)表中搜索以下详细信息。

  • 使用Name搜索具体Employee 细节。
  • 在特定Department中的Employees列表。
  • 在特定Designation中的Employees列表。
  • 去年加入该组织的Employees列表。
  • Salary >= 一些具体的金额的Employees列表。
  • 上面列出的任何这些条件或所有这些条件。

在存储过程中的SELECT查询通过COALESCE函数动态实现WHERE-Clause以获得所需的结果。

2.1 - 使用COALESCE

/* Create Stored Procedure 'sp_EmployeeSelect_Coalesce'. 
Example 2.1 - Using Coalesce */ 

Create Procedure sp_EmployeeSelect_Coalesce
    @EmployeeName NVarchar(100),
    @Department NVarchar(50),
    @Designation NVarchar(50),
    @StartDate DateTime,
    @EndDate DateTime,
    @Salary Decimal(10,2)
        
AS
      Set NoCount ON
 
    Select * From tblEmployees 
    where EmployeeName = Coalesce(@EmployeeName, EmployeeName) AND
        Department = Coalesce(@Department, Department ) AND
        Designation = Coalesce(@Designation, Designation) AND
        JoiningDate >= Coalesce(@StartDate, JoiningDate) AND 
        JoiningDate <= Coalesce(@EndDate, JoiningDate) AND
        Salary >= Coalesce(@Salary, Salary)

    If @@ERROR <> 0 GoTo ErrorHandler
    Set NoCount OFF
    Return(0)
  
ErrorHandler:
    Return(@@ERROR)
GO

在上面的存储过程中,对于WHERE-clause中的每个条件,该COALESCE 函数选择第一个非空值并将其用于比较操作。如果输入参数值为NULL,则coalesce函数返回等于其自身的实际值。使该操作返回特定行。

使用ISNULL

ISNULL是一个用于处理NULL值的T-SQL系统函数,它有两个参数,第一个是检查表达式,第二个参数是替换值,如果检查表达式值是NULL。我们可以说这个ISNULL相当于COALESCE带有两个参数的函数。

基本语法: ISNULL()

ISNULL ( check_expression , replacement_value )

注意replacement_value 必须具有相同的类型check_expresssion

让我们以上面的例子为例,编写使用该ISNULL函数动态构建WHERE-clause 的存储过程。

2.2 - 使用ISNULL

/* Create Stored Procedure 'sp_EmployeeSelect_ISNULL'. 
Example 2.2 - Using IsNull */ 

Create Procedure sp_EmployeeSelect_ISNULL
    @EmployeeName NVarchar(100),
    @Department NVarchar(50),
    @Designation NVarchar(50),
    @StartDate DateTime,
    @EndDate DateTime,
    @Salary Decimal(10,2)
AS
      Set NoCount ON
  
    Select * From tblEmployees 
    where EmployeeName = IsNull(@EmployeeName, EmployeeName) AND
        Department = IsNull(@Department, Department ) AND
        Designation = IsNull(@Designation, Designation) AND
        JoiningDate >= IsNull(@StartDate, JoiningDate) AND 
        JoiningDate <= IsNull(@EndDate, JoiningDate) AND
        Salary >= IsNull(@Salary, Salary)

    If @@ERROR <> 0 GoTo ErrorHandler
    Set NoCount OFF
    Return(0)
  
ErrorHandler:
    Return(@@ERROR)
GO

您可以在示例2.2中看到—— 使用该ISNULL函数动态构建WHERE-clause  。它评估表达式并检查参数值是否为NULL。当此检查表达式返回Non-Null值时,它将在比较操作中使用参数值。当检查表达式返回时null,它使用等于其自身的当前值,并使该操作返回所有行。

使用CASE

CASE 函数等效于SQL Server中的COALESCE函数。它评估条件列表并从多个可能的结果表达式返回一个结果表达式。有两种类型的CASE功能:

  • 简单 CASE
  • 搜索 CASE

基本语法:简单CASE

CASE input_expression
   WHEN (when_expression1 IS NOT NULL) THEN result_expression1
   WHEN (when_expression2 IS NOT NULL) THEN result_expression2
   ...
   WHEN (when_expressionN IS NOT NULL) THEN result_expressionN
   ELSE else_result_expression
END

基本语法:搜索CASE

CASE
   WHEN (boolean_expression1 IS NOT NULL) THEN result_expression1
   WHEN (boolean_expression2 IS NOT NULL) THEN result_expression2
   ...
   WHEN (boolean_expressionN IS NOT NULL) THEN result_expressionN
   ELSE else_result_expression
END

简单的CASE函数将input_expression when_expression进行比较,获得所需的result_expression。搜索的CASE 函数计算一组布尔表达式以获得所需的result_expression。让我们使用相同的示例并编写使用该CASE函数动态构建WHERE-clause 的存储过程。

2.3 - 使用CASE

/* Create Stored Procedure 'sp_EmployeeSelect_Case'. 
Example 2.3 - Using Case-When */ 

Create Procedure sp_EmployeeSelect_Case
    @EmployeeName NVarchar(100),
    @Department NVarchar(50),
    @Designation NVarchar(50),
    @StartDate DateTime,
    @EndDate DateTime,
    @Salary Decimal(10,2)
AS
      Set NoCount ON
  
    Select * From tblEmployees where EmployeeName = 
    Case When @EmployeeName Is Not Null Then @EmployeeName 
    Else EmployeeName End AND Department = 
    Case When @Department Is Not Null Then @Department 
    Else Department End AND Designation = 
    Case When @Designation Is Not Null Then @Designation 
    Else Designation End AND JoiningDate >= 
    Case When @StartDate Is Not Null Then @StartDate 
    Else JoiningDate End AND JoiningDate <= 
    Case When @EndDate Is Not Null Then @EndDate 
    Else JoiningDate End AND Salary >= 
    Case When @Salary Is Not Null Then @Salary 
    Else Salary End 

    If @@ERROR <> 0 GoTo ErrorHandler
    Set NoCount OFF
    Return(0)
  
ErrorHandler:
    Return(@@ERROR)
GO

您可以在示例2.3中看到WHERE-clause是使用该CASE函数动态构建的。它评估表达式并检查参数值是否为NULL。当此布尔表达式返回true时,它将使用比较操作中的参数值。当布尔表达式返回false时,它使用等于其自身的当前值,并使该操作返回所有行。

替代方案

这个替代方案既不使用COALESCE也不使用CASE函数来构建动态 WHERE-clause,而是使用与之相当的逻辑——值得使用它。

2.4 - 替代方案

/* Create Stored Procedure 'sp_EmployeeSelect_Alternate'. 
Example 2.4 - Alternate */ 

Create Procedure sp_EmployeeSelect_Alternate
    @EmployeeName NVarchar(100),
    @Department NVarchar(50),
    @Designation NVarchar(50),
    @StartDate DateTime,
    @EndDate DateTime,
    @Salary Decimal(10,2)
AS 
    Set NoCount ON
  
    SELECT * FROM tblEmployees
    WHERE (@EmployeeName Is Null OR @EmployeeName = EmployeeName) AND
        (@Department Is Null OR @Department = Department) AND
        (@Designation Is Null OR @Designation = Designation) AND
        (@Salary Is Null OR @Salary = Salary) AND
        (@StartDate Is Null OR @EndDate Is Null OR 
        (@StartDate Is Not Null AND @EndDate Is Not Null AND 
        JoiningDate BETWEEN @StartDate AND @EndDate))

    If @@ERROR <> 0 GoTo ErrorHandler
    Set NoCount OFF
    Return(0)
  
ErrorHandler:
    Return(@@ERROR)
GO

结论

希望您学会了如何在静态SQL中实现动态WHERE-Clause。在所有示例中,我展示了如何处理输入参数为NULL的情况。即使它是用于多重比较的不同的非Null值,您仍然可以更有效地使用简单CASE和搜索CASE功能。因此,我得出结论,静态SQL更快,更安全,在大多数情况下,我们不需要使用动态SQL语句。

 

原文地址:https://www.codeproject.com/Articles/21234/Implementing-Dynamic-WHERE-Clause-in-Static-SQL

猜你喜欢

转载自blog.csdn.net/mzl87/article/details/87626179