J.D*_*.D. 2 sql-server hints execution-plan errors sql-server-2016
我有以下(愚蠢地简化的)查询,它利用两个引用同一个表的 CTE 并将它们相互连接:
WITH CTE1 AS
(
SELECT dbo.RemoveNonNumericCharacters(PhoneNumber) AS PhoneNumberCleaned
FROM PhoneNumbersTable
GROUP BY dbo.RemoveNonNumericCharacters(PhoneNumber)
),
CTE2 AS
(
SELECT CTE1.PhoneNumberCleaned
FROM CTE1
INNER HASH JOIN PhoneNumbersTable
ON CTE1.PhoneNumbersCleaned = dbo.RemoveNonNumericCharacters(PhoneNumbersTable.PhoneNumber)
WHERE PhoneNumbersTable.AreaCode IN (718, 212)
)
SELECT PhoneNumberCleaned
FROM CTE2
Run Code Online (Sandbox Code Playgroud)
注意HASH JOIN
里面发生的事情CTE2
。到目前为止,这一切都运作良好。
如果我将以下WHERE
子句添加到最终SELECT
查询中,那么我的整个查询现在变为:
WITH CTE1 AS
(
SELECT dbo.RemoveNonNumericCharacters(PhoneNumber) AS PhoneNumberCleaned
FROM PhoneNumbersTable
GROUP BY dbo.RemoveNonNumericCharacters(PhoneNumber)
),
CTE2 AS
(
SELECT CTE1.PhoneNumberCleaned
FROM CTE1
INNER HASH JOIN PhoneNumbersTable
ON CTE1.PhoneNumbersCleaned = dbo.RemoveNonNumericCharacters(PhoneNumbersTable.PhoneNumber)
WHERE PhoneNumbersTable.AreaCode IN (718, 212)
)
SELECT PhoneNumberCleaned
FROM CTE2
WHERE PhoneNumberCleaned = 'SomePhoneNumberInTheResultSet' -- E.g. 7183998888
Run Code Online (Sandbox Code Playgroud)
然后我得到了经典的错误:
消息 8622,级别 16,状态 1,第 50 行 由于此查询中定义的提示,查询处理器无法生成查询计划。重新提交查询,不指定任何提示,也不使用 SET FORCEPLAN。
仅当我在子句中使用的值WHERE
实际存在于结果集中时才会发生这种情况。如果我选择任何不存在的值,那么我不会收到上述错误。
现在显然我的例子对于正在发生的事情来说有点愚蠢,我可以用几种不同的方式重写它来可能修复它,但我更好奇为什么会发生这种情况?如果 SQL Server 引擎能够生成返回所有记录的查询计划,为什么它无法在该查询计划的末尾为我在子句中筛选的标量值添加额外的筛选运算符WHERE
?
这是我的函数的黑盒dbo.RemoveNonNumericCharacters
代码(注意我没有写这个):
CREATE FUNCTION [dbo].[RemoveNonNumericCharacters] (@strText VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
WHILE PATINDEX('%[^0-9]%', @strText) > 0
BEGIN
SET @strText = STUFF(@strText, PATINDEX('%[^0-9]%', @strText), 1, '')
END
RETURN @strText
END
Run Code Online (Sandbox Code Playgroud)
PhoneNumber
另请注意,中的列PhoneNumbersTable
的类型为VARCHAR(20)
。
该问题不包含复制脚本,但经常会出现此错误,因为隐含谓词使连接谓词变得多余。
换句话说,查询规范中的逻辑含义将内部联接转换为逻辑叉积(简化后)。这不一定是坏事(因为人们倾向于认为叉积是坏事),它只是意味着可以通过这种方式简化查询规范。
哈希连接需要相等谓词。当优化器考虑连接实现时,查询树中没有合适的谓词,编译将失败并出现错误。在理想的世界中,优化器也许不会简化满足提示所需的连接谓词。
这是我根据问题的文字内容做出的有根据的猜测。如果您需要更详细的解释,请提供完整的重现并指定环境。
作为一个附带问题,除了Erik Darling 的建议之外,这里还有一个适用于 SQL Server 2016 的确定性纯数字标量函数:
CREATE FUNCTION dbo.RemoveNonNumericCharacters
(@string nvarchar(4000))
RETURNS
nvarchar(4000)
WITH
SCHEMABINDING,
RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN
ISNULL(
CONVERT(nvarchar(4000),
(
SELECT This.ch AS [text()]
FROM OPENJSON(N'[1' + REPLICATE(N',1', LEN(@string) - 1) + N']') AS J
CROSS APPLY (SELECT 1 + CONVERT(integer, J.[Key])) AS V (v)
OUTER APPLY (SELECT SUBSTRING(@string COLLATE Latin1_General_100_BIN2, V.v, 1)) AS This (ch)
WHERE This.ch COLLATE Latin1_General_100_BIN2 LIKE N'[0123456789]'
ORDER BY V.v
FOR XML PATH (N'')
)),
N'');
END;
Run Code Online (Sandbox Code Playgroud)
这可能不是效率的定论,但很有趣。您可以使用 来将函数转换为确定性函数CHARINDEX
。
对于 SQL Server 2017 及更高版本:
CREATE FUNCTION dbo.RemoveNonNumericCharacters
(@string nvarchar(4000))
RETURNS
nvarchar(4000)
WITH
SCHEMABINDING,
--INLINE = ON, /* for 2019 */
RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN
REPLACE(
TRANSLATE(@string COLLATE Latin1_General_100_BIN2,
TRANSLATE(@string COLLATE Latin1_General_100_BIN2,
N'0123456789',
N'XXXXXXXXXX'),
REPLICATE(N'X', LEN(@string))),
N'X', N'');
END;
Run Code Online (Sandbox Code Playgroud)
两者都很容易转换为内联表值函数。
归档时间: |
|
查看次数: |
866 次 |
最近记录: |