在SQL过程中创建where子句时,我有一个最佳用途的问题。 我在where子句中使用Coalesce,使用isnull或语句使用Coalesce,使用sp_executesql使用Coalesce,使用三种不同的方式编写了查询。
聚结:
WHERE ClientID = COALESCE(@Client, ClientID) AND
AccessPersonID = COALESCE(@AccessPerson, AccessPersonID)
IsNull Or:
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%准确。
这是一个非常简单的过程我只想弄清楚编写这样的程序的方法是什么。我会减少动态,因为它是最准确的。
答案 0 :(得分:1)
正确答案是'动态'选项。您可以保留参数,因为它可以防止SQL注入(无论如何都在此层)。
“动态”是最好的原因是因为它将创建一个最适合给定查询的查询计划。根据您的示例,您可能最多可以获得3个此查询计划,具体取决于&gt; 0,但每个生成的计划将针对该场景进行优化(它们将省略不必要的参数比较)。
其他两种样式将生成一个计划(每个),并且仅针对您在此时使用的参数进行优化。每个后续执行都将使用旧计划,并且可能使用您未调用的参数进行缓存。
'动态'不像其他两个选项那样是干净的代码,但是为了提高性能,它每次都会为您提供最佳的查询计划。
答案 1 :(得分:1)
动态SQL的操作范围与你的sproc不同,所以即使你在sproc中声明了一个变量,你也必须在动态SQL中重新声明它。或者将其结合到声明中。但是你 也应该在你的动态SQL和你的sproc中做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
注意:出于演示目的,我还必须修改上面的临时表并使其成为全局临时表而不是本地临时表,因为我是从动态SQL调用它。它存在于不同的范围内。在你完成之后别忘了清理它。 : - )
答案 2 :(得分:0)
如果任何一个值为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)。换句话说,如果IS为NULL,则使用b。
而且,这一切都取决于你最终想做什么。 sp_ExecuteSQL是以MS为中心的,因此如果您不打算将其移植到任何其他数据库,则可以使用它。但老实说,在15年后,我可能已经将应用程序从一个数据库移植到另一个数据库不到十几次。更重要的是,如果您正在编写将在其他系统上安装它的其他人使用的应用程序,但如果它是一个封闭的系统,您使用的数据库的好处通常超过缺乏可移植性。
答案 3 :(得分:0)
我可能应该包含查询的另一部分
对于ISNULL和COALESCE,我将值0转换为null,在动态中,我将if值保留为0。这就是为什么看起来有点不同。
从我看到的情况来看,COALESCE似乎始终是表现最差的。 令人惊讶的是,我测试的ISNULL和动态非常类似,ISNULL版本在大多数情况下略胜一筹。
在大多数情况下,它已经修改了需要添加的索引,并且在大多数情况下,索引最大程度地改进了查询,但是在添加之后,ISNULL和Dynamic仍然比COALESCE表现更好。
此外,我无法看到我们在近期或遥远的未来从MSSQL转换。