在 MSSQL 查询中使用多个 JSON_VALUE-s 会损害性能

Tig*_*yan 4 sql database sql-server select json

我在 sql server 数据库中有一个表,其中有 json 类型的列。

Table - SomeTable
Id | Properties
1  | {"field1":"value1", "field2":"value2", "field3":"value3", "field4":"value4"}
2  | {"field1":"value1", "field2":"value2", "field3":"value3", "field4":"value4"}
...|...
Run Code Online (Sandbox Code Playgroud)

我编写了选择查询,它分别选择每个字段的值:

SELECT
    JSON_VALUE(Properties, '$.field1') as field1,
    JSON_VALUE(Properties, '$.field2') as field2,
    JSON_VALUE(Properties, '$.field3') as field3,
    JSON_VALUE(Properties, '$.field4') as field4
FROM SomeTable
Run Code Online (Sandbox Code Playgroud)

我在微软的文档中找到了这种方法(https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver15

在查询中写入许多 JSON_VALUE-s 会损害性能吗?SQL 是否对查询中写入的每个 JSON_VALUE 进行字符串反序列化。

Zho*_*rov 6

您可以尝试使用显式模式,通过一次函数调用OPENJSON()来解析存储在列中的 JSON (而不是四次或更多调用):PropertiesJSON_VALUE()

桌子:

CREATE TABLE SomeTable (
   Id int,
   Properties varchar(1000)
)
INSERT INTO SomeTable (Id, Properties)
VALUES
   (1, '{"field1":"value1", "field2":"value2", "field3":"value3", "field4":"value4"}'),
   (2, '{"field1":"value1", "field2":"value2", "field3":"value3", "field4":"value4"}')
Run Code Online (Sandbox Code Playgroud)

陈述:

SELECT s.Id, j.*
FROM SomeTable s
CROSS APPLY OPENJSON(s.Properties) WITH (
   field1 varchar(100) '$.field1',
   field2 varchar(100) '$.field2',
   field3 varchar(100) '$.field3',
   field4 varchar(100) '$.field4'
) j
Run Code Online (Sandbox Code Playgroud)

结果:

Id  field1  field2  field3  field4
----------------------------------
1   value1  value2  value3  value4
2   value1  value2  value3  value4
Run Code Online (Sandbox Code Playgroud)

作为补充说明, 的结果JSON_VALUE()是类型 的标量值nvarchar(4000)。通过OPENJSON()显式模式,您可以为返回的列定义适当的数据类型。

  • 这就对了 !感谢您的回复 (2认同)

use*_*983 6

我想我会对你的问题做一个测试:

在查询中写入许多 JSON_VALUE-s 会损害性能吗?SQL 是否对查询中写入的每个 JSON_VALUE 进行字符串反序列化。

因此,我创建了以下脚本并在我的家庭工作站上进行了测试:

/*
Id | Properties
1  | {"field1":"value1", "field2":"value2", "field3":"value3", "field4":"value4"}
2  | {"field1":"value1", "field2":"value2", "field3":"value3", "field4":"value4"}
*/

CREATE TABLE dbo.YourTable (ID int IDENTITY,
                            Properties nvarchar(4000));

WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1, N N2, N N3, N N4, N N5, N N6)
INSERT INTO dbo.YourTable (Properties)
SELECT N'{"field1":"value1", "field2":"value2", "field3":"value3", "field4":"value4"}'
FROM Tally;
GO

DECLARE @StartTime datetime2(7) = SYSDATETIME();

SELECT
    JSON_VALUE(Properties, '$.field1') as field1,
    JSON_VALUE(Properties, '$.field2') as field2,
    JSON_VALUE(Properties, '$.field3') as field3,
    JSON_VALUE(Properties, '$.field4') as field4
FROM dbo.YourTable;

PRINT DATEDIFF(MILLISECOND,@StartTime, SYSDATETIME());

SET @StartTime = SYSDATETIME();

SELECT YT.Id,
       OJ.field1,
       OJ.field2,
       OJ.field3,
       OJ.field4
FROM dbo.YourTable YT
     CROSS APPLY OPENJSON(YT.Properties)
                 WITH(field1 varchar(50),
                      field2 varchar(50),
                      field3 varchar(50),
                      field4 varchar(50)) OJ;

PRINT DATEDIFF(MILLISECOND,@StartTime, SYSDATETIME());

GO 10

DROP TABLE dbo.YourTable
Run Code Online (Sandbox Code Playgroud)

这使用了你的解决方案,JSON_VALUE还有一个OPENJSON解决方案(我在 Zhorov 发布他的答案的同时写的)。在我的工作站上,这导致消息输出如下:

(1000000 rows affected)
5273
(1000000 rows affected)
3560
(1000000 rows affected)
5196
(1000000 rows affected)
3329
(1000000 rows affected)
5097
(1000000 rows affected)
3320
(1000000 rows affected)
5219
(1000000 rows affected)
3379
(1000000 rows affected)
5133
(1000000 rows affected)
3239
(1000000 rows affected)
5137
(1000000 rows affected)
3352
(1000000 rows affected)
5080
(1000000 rows affected)
3348
(1000000 rows affected)
5126
(1000000 rows affected)
3320
(1000000 rows affected)
5146
(1000000 rows affected)
3360
(1000000 rows affected)
5310
(1000000 rows affected)
3354
Run Code Online (Sandbox Code Playgroud)

请注意,第二行(带有OPENJSON)每次运行大约快 2 秒。然而,虽然有 1,000,000 百万行,但它确实证实了 JSON 的单次解析比 4 快得多。