不正确的 uniqueidentifier 在没有主键的情况下工作,但在有一个时失败

Ric*_*ner 6 sql-server primary-key uniqueidentifier

因此,我们最近对以 auniqueidentifier作为主键的表中的插入运行了一些逻辑测试。这是我的测试查询;

IF OBJECT_ID('tempdb..#DatabaseTable') IS NOT NULL DROP TABLE #DatabaseTable
CREATE TABLE #DatabaseTable
        ([ID] [uniqueidentifier] NOT NULL PRIMARY KEY,
        [LastUpdate] [datetime] NOT NULL,
        [Locked] [tinyint] NOT NULL)

IF OBJECT_ID('tempdb..#temptable') IS NOT NULL  DROP TABLE #temptable
CREATE TABLE #temptable (ID varchar(36), Data nvarchar(max))

INSERT INTO #temptable (ID, Data) 
VALUES ('g078f19e-e150-4bb9-b5f4-b20b3fc64016', 'text goes here')

SELECT tmp.ID ,tmp.Data
FROM #temptable tmp 
LEFT JOIN #DatabaseTable db
    ON tmp.ID = db.ID
WHERE db.ID is null
Run Code Online (Sandbox Code Playgroud)

这将失败,因为 uniqueidentifier 无效,因为它包含不正确的 'g' 值,该值超出了此数据类型的可接受范围。发生这种情况是因为开发人员为了测试手动更改了它,因为他知道目标表中不存在它,但不知道有效的字符列表。

这是它抛出的错误;

(1 row(s) affected)
Msg 8169, Level 16, State 2, Line 13
Conversion failed when converting from a character string to uniqueidentifier.
Run Code Online (Sandbox Code Playgroud)

这是我期望的行为。但是,当我在测试中搞砸时,我删除了 #DatabaseTable 上的主键,它运行得很好。现在我检查了执行计划,期望它转换 uniqueidentifier 以匹配varchar(36),但是我看到了这个计算标量;

CONVERT_IMPLICIT(uniqueidentifier,[tempdb].[dbo].[#temptable].[ID] as [tmp].[ID],0)
Run Code Online (Sandbox Code Playgroud)

我似乎无法从微软找到任何关于 CONVERT_IMPLICIT 实际语法的文档,所以我假设最后的零是它在转换失败时被替换的内容,这是否是一个正确的假设(与ISNULL/相同COALESCE)?另外,为什么在没有 PK 的情况下这可以正常工作,但会失败?

Mar*_*ith 7

我能够重现这个。

两个计划都有#temptable驱动嵌套循环连接的行。

#DatabaseTable有主键时,有一个索引要查找,所以连接内部有一个查找。有必要将 varchar 强制转换为 uniqueidentifier 以获取要查找的值,此时尝试转换无效值失败。

当没有主键时,计划会在嵌套循环内部进行扫描。它可以推迟评估,直到有一行要比较,扫描后没有行输出#DatabaseTable(因为它是空的),所以CONVERT_IMPLICIT永远不需要评估。

计算标量的位置可能会让您不以为然,但请参阅计算标量、表达式和执行计划性能以了解更多相关信息。