隐式类型转换:JSON_VALUE()与TRY_CAST(JSON_VALUE())

时间:2019-04-21 12:52:27

标签: sql-server tsql

我想创建一个通用链接表来链接我们拥有的记录和另一个数据库的记录。我可以为源数据库中的每个表创建一个链接表。但是,由于现在有对JSON的支持,所以我认为这可能是研究该支持的好用例。

因此,我没有创建一个名为Link{SourceTableName}的表,而是创建了一个表,其中有一个GUID代表我们这边的ID,还有一个NVARCHAR(MAX)列来表示{源表的主键。我将主键另存为JSON,因为我必须支持复合主键。如果我想要源表中的数据,可以将JSON的{​​{1}}列与源表中的主键一起JOIN

由于JSON返回了JSON_VALUE,所以当源表的主键不是预期的JSON_VALUE时,我将得到隐式类型转换。但是,如果我用NVARCHAR(MAX)来封装NVARCHAR(MAX),则隐式转换不再是必需的。我决定这样做,但是我好奇是否由于额外的函数调用而导致性能下降。事情变得奇怪了...

下面是源表上的简单JSON_VALUETRY_CASTSELECT *的比较。

enter image description here enter image description here enter image description here

enter image description here

我创建了一个示例查询来重现该问题。

SELECT * joined on TRY_CAST(JSON_VALUE())

enter image description here enter image description here

如您所见,第一个SELECT * joined on JSON_VALUE()查询和第二个CREATE TABLE [#Link] ( [Id] UNIQUEIDENTIFIER NOT NULL CONSTRAINT [DF_Link_Id] DEFAULT(NEWID()), [LinkId] NVARCHAR(MAX) NOT NULL, CONSTRAINT [PK_Link] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 100) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] CREATE TABLE [#Customer] ( [Id] CHAR(7) NOT NULL, [Name] NVARCHAR(200) NULL, CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 100) ON [PRIMARY]) ON [PRIMARY] DECLARE @i INT = 0 WHILE @i < 20 BEGIN WITH seed AS ( SELECT (@i * 32767) + 1 AS n UNION ALL SELECT n+1 FROM seed WHERE n+1<=(@i * 32767) + 32767 ) INSERT INTO [#Customer] SELECT RIGHT('0000000'+CAST([n] AS VARCHAR(7)),7) ,RIGHT('0000000'+CAST([n] AS VARCHAR(7)),7) FROM seed OPTION(maxrecursion 32767) SET @i = @i + 1 END INSERT INTO [#Link] ([LinkId]) SELECT [value] FROM OPENJSON((SELECT [Id] FROM [#Customer] FOR JSON PATH)) SELECT TOP 10 * FROM [#Customer] SELECT TOP 10 * FROM [#Customer] JOIN [#Link] ON [#Customer].[Id] = TRY_CAST(JSON_VALUE([#Link].[LinkId], '$.Id') AS char(7)) SELECT TOP 10 * FROM [#Customer] JOIN [#Link] ON [#Customer].[Id] = JSON_VALUE([#Link].[LinkId], '$.Id') SELECT TOP 100 * FROM [#Customer] SELECT TOP 100 * FROM [#Customer] JOIN [#Link] ON [#Customer].[Id] = TRY_CAST(JSON_VALUE([#Link].[LinkId], '$.Id') AS char(7)) SELECT TOP 100 * FROM [#Customer] JOIN [#Link] ON [#Customer].[Id] = JSON_VALUE([#Link].[LinkId], '$.Id') DROP TABLE [#Customer] DROP TABLE [#Link] 查询的执行计划有所不同。在执行计划的破译中,我不是英雄,但有区别的事实很有趣。

现在有一个警告,抱怨两个执行计划中都存在隐式类型转换。这一定是造成执行计划不同的原因,因为将JSON_VALUE()的数据类型更改为NVARCHAR(7)时,TOP 10和TOP 100的执行计划保持不变。

我知道CONVERT_IMPLICIT是性能杀手,但我并没有期望看到TOP 10和TOP 100之间存在差异。由于第二个查询比第一个查询快,因此第一个查询很可能像快速。为什么查询优化器不总是选择第二个执行计划,还是我要求太多?

0 个答案:

没有答案