Я хочу создать общую таблицу ссылок, чтобы связать имеющиеся у нас записи с записями другой базы данных.Я мог бы создать таблицу ссылок для каждой таблицы в исходной базе данных.Однако, поскольку существует поддержка JSON
, теперь я подумал, что, возможно, это хороший вариант использования для изучения этой поддержки.
Поэтому вместо создания нескольких таблиц с именем Link{SourceTableName}
я создаю одну таблицу с GUID
для представления идентификатора на нашей стороне и столбца NVARCHAR(MAX)
для представления JSON
, содержащего первичный ключ исходной таблицы.Я сохраняю первичный ключ как JSON
, потому что должен поддерживать составные первичные ключи.Если мне нужны данные в исходной таблице, я могу 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()
запроса и второго JSON_VALUE()
запроса.Я не герой в расшифровке планов выполнения, но интересен только факт, что есть разница.
Присутствует предупреждение с жалобой на неявное преобразование типов, присутствующее в обоих планах выполнения.Это должно быть причиной различий в планах выполнения, потому что при изменении типа данных [#Customer].[Id]
на NVARCHAR (7) планы выполнения TOP 10 и TOP 100 остаются прежними.
Я знаючто CONVERT_IMPLICIT снижает производительность, но я бы не ожидал увидеть разницу между TOP 10 и TOP 100. Поскольку второй запрос быстрее первого, очевидно, что первый может быть быстрее.Почему оптимизатор запросов не всегда выбирает второй план выполнения или я слишком много спрашиваю?