sql-server - 隐式类型转换:JSON_VALUE() vs TRY_CAST(JSON_VALUE())
问题描述
我想创建一个通用链接表来链接我们拥有的记录,以及另一个数据库的记录。我可以为源数据库中的每个表创建一个链接表。但是,由于现在有支持,JSON
我认为这可能是一个很好的用例来调查这种支持。
因此,我没有创建多个名为的表,而是Link{SourceTableName}
创建了一个表,其中一个GUID
代表我们这边的 Id,一个NVARCHAR(MAX)
列代表JSON
包含源表的主键。我保存主键是JSON
因为我必须支持复合主键。如果我想要源表中的数据,我可以JOIN
将JSON
列与JSON_VALUE
源表中的主键一起使用。
由于JSON_VALUE
返回 an当源表的主键不是预期NVARCHAR(MAX)
的时,我将获得隐式类型转换。NVARCHAR(MAX)
但是,如果我将其包装JSON_VALUE
为TRY_CAST
不再需要隐式转换。我决定这样做,但我很好奇是否由于额外的函数调用而导致性能损失。这就是事情变得奇怪的地方......
下面是SELECT *
源表上的 a simple 与 aSELECT * joined on TRY_CAST(JSON_VALUE())
与 a的比较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()
查询和第二个JSON_VALUE()
查询的执行计划之间存在差异。我不是破译执行计划的英雄,但有趣的是存在差异这一事实。
存在一个警告,抱怨两个执行计划中都存在隐式类型转换。这一定是导致执行计划不同的原因,因为当将数据类型更改[#Customer].[Id]
为 NVARCHAR(7) 时,TOP 10 和 TOP 100 的执行计划保持不变。
我知道 CONVERT_IMPLICIT 是性能杀手,但我没想到会看到 TOP 10 和 TOP 100 之间的差异。由于第二个查询比第一个查询快,显然第一个查询也有可能同样快。为什么查询优化器不总是选择第二个执行计划,或者我要求太多了?
解决方案
推荐阅读
- iis - 如果您通过域名访问,则会出现 Windows 身份验证提示
- light-inject - 使用 LightInject,如何在不注册一堆工厂的情况下将参数传递给子依赖项?
- python - Python - 在几个较小的数据帧中切片数据帧
- php - 如何在 Vagrant 和 Homestead 中回滚 PHP 版本?
- html - 文本对齐适用于一个标签但不适用于另一个标签?
- wordpress - 无法同时从本地和外部访问 Wordpress 站点
- r - 如何绘制角度在 0-360 度之间的线
- c# - Convert Char[] to Int,战争纸牌游戏
- python - 有没有更好的方法来记住 Python 中先前给出的可选方法参数?
- ascii - 是否有各种空格的编译:ascii 和非 ascii 空格?