SQL Where 子句 Coalese vs ISNULL VS Dynamic
SQL Where clause Coalese vs ISNULL VS Dynamic
在 SQL 过程中创建 where 子句时,我有一个最佳使用问题。
我用三种不同的方式编写了一个查询,一种是在 where 子句中使用 Coalesce,一种是使用 isnull or 语句,另一种是动态使用 sp_executesql.
合并:
WHERE ClientID = COALESCE(@Client, ClientID) AND
AccessPersonID = COALESCE(@AccessPerson, AccessPersonID)
IsNull 或:
WHERE (@Client IS NULL OR @Client = ClientID)
AND (@AccessPerson IS NULL OR @AccessPerson= AccessPersonID)
动态地:
SET @sql = @sql + Char(13) + Char(10) + N'WHERE 1 = 1';
IF @Client <> 0
BEGIN
SET @sql = @sql + Char(13) + Char(10) + N' AND ClientID = @Client '
END
IF @AccessPerson <> 0
BEGIN
SET @sql = @sql + Char(13) + Char(10) + N' AND AccessPersonID = @AccessPerson '
END
当我使用 SQL Sentry Plan Explorer 时,结果显示 Coalesce 的估计值最好,但在估计值和实际值之间最不准确。其中动态估计最差,但与实际情况 100% 准确。
这是一个非常简单的程序,我只是想弄清楚编写这样的程序的最佳方法是什么。我认为动态是最准确的方法。
正确答案是 'dynamic' 选项。保留参数很好,因为它可以防止 SQL 注入(无论如何在这一层)。
'dynamic' 之所以最好,是因为它将创建最适合给定查询的查询计划。对于您的示例,您可能会为此查询获得最多 3 个计划,具体取决于哪些参数 > 0,但生成的每个计划都将针对该场景进行优化(它们将省略不必要的参数比较)。
其他两种样式将生成一个计划(每个),并且只会针对您当时使用的参数进行优化。每个后续执行都将使用旧计划,并且可能会使用您未调用的参数进行缓存。
'Dynamic' 不像其他两个选项那样代码简洁,但为了性能,它每次都会为您提供最佳查询计划。
如果任一值为 NULL,您的前两个语句不会做完全相同的事情。
http://sqlfiddle.com/#!9/d0aa3/4
IF OBJECT_ID (N'tempdb..#TestClientID', N'U') IS NOT NULL
DROP TABLE #TestClientID;
GO
CREATE TABLE #TestClientID ( ClientID int , AccessPersonID int )
INSERT INTO #TestClientID (ClientID, AccessPersonID)
SELECT 1,1 UNION ALL
SELECT NULL,1 UNION ALL
SELECT 1,NULL UNION ALL
SELECT 0,0
DECLARE @ClientID int = NULL
DECLARE @AccessPersonID int = 1
SELECT * FROM #TestClientID
WHERE ClientID = COALESCE(@ClientID, ClientID)
AND AccessPersonID = COALESCE(@AccessPersonID, AccessPersonID)
SELECT * FROM #TestClientID
WHERE (@ClientID IS NULL OR @ClientID = ClientID)
AND (@AccessPersonID IS NULL OR @AccessPersonID = AccessPersonID)
也就是说,如果您要消除 NULL 输入值,请使用 COALESCE()。进行比较时,NULL 会变得很奇怪。 COALESCE(a,b) 更类似于 MS SQL 的 ISNULL(a,b)。换句话说,如果 a IS NULL,则使用 b.
再说一遍,这真的完全取决于您最终想要做什么。 sp_ExecuteSQL 以 MS 为中心,因此如果您不打算将其移植到任何其他数据库,则可以使用它。但老实说,在 15 年中,我将应用程序从一个数据库移植到另一个数据库的次数可能还不到十几次。如果您编写的应用程序将被安装在不同系统上的其他人使用,则这一点更为重要,但如果它是一个封闭的系统,那么您使用的数据库的好处通常会超过可移植性的不足。
并且动态 SQL 在与您的存储过程不同的范围内运行,因此即使您在存储过程中声明了一个变量,您也必须在动态 SQL 中重新声明它。或者将其连接到语句中。但是你 应该 也在你的动态 SQL 和你的存储过程中进行 NULL 检查,因为 NULL 不等于 0 也不等于 0。你可以'不要比较它,因为它不存在。 :-S
DECLARE @Client int = 1
, @AccessPerson int = NULL
;
DECLARE @sql nvarchar(2000) = N'SELECT * FROM ##TestClientID WHERE 1=1'
;
IF @Client <> 0
BEGIN
SET @sql = CONCAT(@sql, N' AND ClientID = ', CONVERT(nvarchar(10), @Client))
END
;
IF @AccessPerson <> 0
BEGIN
SET @sql = CONCAT(@sql, N' AND AccessPersonID =', CONVERT(nvarchar(10), @AccessPerson))
END
;
PRINT @sql
EXEC sp_ExecuteSQL @sql
注意:出于演示目的,我还必须修改上面的临时文件 table 并将其设为全局临时文件而不是本地临时文件,因为我是从动态 SQL 调用它的。它存在于不同的范围。完成后不要忘记清理它。 :-)
我可能应该再包含一个查询部分
对于 ISNULL 和 COALESCE,我将值 0 转换为 null,而在动态中,我将 if 子句的值保留为 0。这就是为什么看起来有点不同。
据我所见,COALESCE 似乎始终是表现最差的。
令人惊讶的是,根据我的测试,ISNULL 和 dynamic 非常相似,ISNULL 版本在大多数情况下略好一些。
在大多数情况下,它包含需要添加的索引,并且在大多数情况下,索引对查询的改进最大,但在添加它们之后,ISNULL 和 Dynamic 仍然比 COALESCE 执行得更好。
而且我看不到我们在近期或遥远的将来从 MSSQL 切换。
在 SQL 过程中创建 where 子句时,我有一个最佳使用问题。 我用三种不同的方式编写了一个查询,一种是在 where 子句中使用 Coalesce,一种是使用 isnull or 语句,另一种是动态使用 sp_executesql.
合并:
WHERE ClientID = COALESCE(@Client, ClientID) AND
AccessPersonID = COALESCE(@AccessPerson, AccessPersonID)
IsNull 或:
WHERE (@Client IS NULL OR @Client = ClientID)
AND (@AccessPerson IS NULL OR @AccessPerson= AccessPersonID)
动态地:
SET @sql = @sql + Char(13) + Char(10) + N'WHERE 1 = 1';
IF @Client <> 0
BEGIN
SET @sql = @sql + Char(13) + Char(10) + N' AND ClientID = @Client '
END
IF @AccessPerson <> 0
BEGIN
SET @sql = @sql + Char(13) + Char(10) + N' AND AccessPersonID = @AccessPerson '
END
当我使用 SQL Sentry Plan Explorer 时,结果显示 Coalesce 的估计值最好,但在估计值和实际值之间最不准确。其中动态估计最差,但与实际情况 100% 准确。
这是一个非常简单的程序,我只是想弄清楚编写这样的程序的最佳方法是什么。我认为动态是最准确的方法。
正确答案是 'dynamic' 选项。保留参数很好,因为它可以防止 SQL 注入(无论如何在这一层)。
'dynamic' 之所以最好,是因为它将创建最适合给定查询的查询计划。对于您的示例,您可能会为此查询获得最多 3 个计划,具体取决于哪些参数 > 0,但生成的每个计划都将针对该场景进行优化(它们将省略不必要的参数比较)。
其他两种样式将生成一个计划(每个),并且只会针对您当时使用的参数进行优化。每个后续执行都将使用旧计划,并且可能会使用您未调用的参数进行缓存。
'Dynamic' 不像其他两个选项那样代码简洁,但为了性能,它每次都会为您提供最佳查询计划。
如果任一值为 NULL,您的前两个语句不会做完全相同的事情。
http://sqlfiddle.com/#!9/d0aa3/4
IF OBJECT_ID (N'tempdb..#TestClientID', N'U') IS NOT NULL
DROP TABLE #TestClientID;
GO
CREATE TABLE #TestClientID ( ClientID int , AccessPersonID int )
INSERT INTO #TestClientID (ClientID, AccessPersonID)
SELECT 1,1 UNION ALL
SELECT NULL,1 UNION ALL
SELECT 1,NULL UNION ALL
SELECT 0,0
DECLARE @ClientID int = NULL
DECLARE @AccessPersonID int = 1
SELECT * FROM #TestClientID
WHERE ClientID = COALESCE(@ClientID, ClientID)
AND AccessPersonID = COALESCE(@AccessPersonID, AccessPersonID)
SELECT * FROM #TestClientID
WHERE (@ClientID IS NULL OR @ClientID = ClientID)
AND (@AccessPersonID IS NULL OR @AccessPersonID = AccessPersonID)
也就是说,如果您要消除 NULL 输入值,请使用 COALESCE()。进行比较时,NULL 会变得很奇怪。 COALESCE(a,b) 更类似于 MS SQL 的 ISNULL(a,b)。换句话说,如果 a IS NULL,则使用 b.
再说一遍,这真的完全取决于您最终想要做什么。 sp_ExecuteSQL 以 MS 为中心,因此如果您不打算将其移植到任何其他数据库,则可以使用它。但老实说,在 15 年中,我将应用程序从一个数据库移植到另一个数据库的次数可能还不到十几次。如果您编写的应用程序将被安装在不同系统上的其他人使用,则这一点更为重要,但如果它是一个封闭的系统,那么您使用的数据库的好处通常会超过可移植性的不足。
并且动态 SQL 在与您的存储过程不同的范围内运行,因此即使您在存储过程中声明了一个变量,您也必须在动态 SQL 中重新声明它。或者将其连接到语句中。但是你 应该 也在你的动态 SQL 和你的存储过程中进行 NULL 检查,因为 NULL 不等于 0 也不等于 0。你可以'不要比较它,因为它不存在。 :-S
DECLARE @Client int = 1
, @AccessPerson int = NULL
;
DECLARE @sql nvarchar(2000) = N'SELECT * FROM ##TestClientID WHERE 1=1'
;
IF @Client <> 0
BEGIN
SET @sql = CONCAT(@sql, N' AND ClientID = ', CONVERT(nvarchar(10), @Client))
END
;
IF @AccessPerson <> 0
BEGIN
SET @sql = CONCAT(@sql, N' AND AccessPersonID =', CONVERT(nvarchar(10), @AccessPerson))
END
;
PRINT @sql
EXEC sp_ExecuteSQL @sql
注意:出于演示目的,我还必须修改上面的临时文件 table 并将其设为全局临时文件而不是本地临时文件,因为我是从动态 SQL 调用它的。它存在于不同的范围。完成后不要忘记清理它。 :-)
我可能应该再包含一个查询部分
对于 ISNULL 和 COALESCE,我将值 0 转换为 null,而在动态中,我将 if 子句的值保留为 0。这就是为什么看起来有点不同。
据我所见,COALESCE 似乎始终是表现最差的。 令人惊讶的是,根据我的测试,ISNULL 和 dynamic 非常相似,ISNULL 版本在大多数情况下略好一些。
在大多数情况下,它包含需要添加的索引,并且在大多数情况下,索引对查询的改进最大,但在添加它们之后,ISNULL 和 Dynamic 仍然比 COALESCE 执行得更好。
而且我看不到我们在近期或遥远的将来从 MSSQL 切换。