我想创建一个通用链接表来链接我们拥有的记录和另一个数据库的记录。我可以为源数据库中的每个表创建一个链接表。但是,由于现在有对JSON
的支持,所以我认为这可能是研究该支持的好用例。
因此,我没有创建一个名为Link{SourceTableName}
的表,而是创建了一个表,其中有一个GUID
代表我们这边的ID,还有一个NVARCHAR(MAX)
列来表示{源表的主键。我将主键另存为JSON
,因为我必须支持复合主键。如果我想要源表中的数据,可以将JSON
的{{1}}列与源表中的主键一起JOIN
。
由于JSON
返回了JSON_VALUE
,所以当源表的主键不是预期的JSON_VALUE
时,我将得到隐式类型转换。但是,如果我用NVARCHAR(MAX)
来封装NVARCHAR(MAX)
,则隐式转换不再是必需的。我决定这样做,但是我好奇是否由于额外的函数调用而导致性能下降。事情变得奇怪了...
下面是源表上的简单JSON_VALUE
与TRY_CAST
和SELECT *
的比较。
我创建了一个示例查询来重现该问题。
SELECT * joined on TRY_CAST(JSON_VALUE())
如您所见,第一个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之间存在差异。由于第二个查询比第一个查询快,因此第一个查询很可能像快速。为什么查询优化器不总是选择第二个执行计划,还是我要求太多?