【问题标题】:T-SQL - search in filtered JSON arrayT-SQL - 在过滤的 JSON 数组中搜索
【发布时间】:2019-09-09 14:11:37
【问题描述】:

SQL Server 2017。

OrderData 具有存储 JSON 的列 DataProperties。 JSON 示例存储在那里:

{
 "Input": {
   "OrderId": "abc",
   "Data": [
     {
       "Key": "Files",
       "Value": [
         "test.txt",
         "whatever.jpg"
       ]
     },
     {
       "Key": "Other",
       "Value": [
         "a"
       ]
     }
   ]
 }
}

所以,它是一个带有 Input 对象的对象,它具有 KVP 的 Data 数组 - 充满了带有 Key 字符串和 Value 字符串数组的对象。

我的问题 - 我需要根据示例 JSON 中的文件中的值查询行 - 与 %text% 匹配的简单 LIKE。

此查询有效:

SELECT TOP 10 *
FROM OrderData CROSS APPLY OPENJSON(DataProperties,'$.Input.Data') dat
WHERE JSON_VALUE(dat.value, '$.Key') = 'Files' and dat.[key] = 0 
AND JSON_QUERY(dat.value, '$.Value') LIKE '%2%'

问题是这个查询很慢,这不足为奇。

如何让它更快?

  1. 我无法使用 JSON_VALUE 创建计算列,因为我需要在数组中进行过滤。
  2. 我无法在“$.Input.Data”或“$.Input.Data[0].Values”上使用 JSON_QUERY 创建计算列 - 因为我需要在此数组中使用 Key == "Files" 指定特定数组项。

我已经搜索过,但您似乎无法创建也过滤数据的计算列,就像这次尝试一样:

ALTER TABLE OrderData
 ADD aaaTest AS (select JSON_QUERY(dat.value, '$.Value')
 OPENJSON(DataProperties,'$.Input.Data') dat
WHERE JSON_VALUE(dat.value, '$.Key') = 'Files' and dat.[key] = 0 );

错误:Subqueries are not allowed in this context. Only scalar expressions are allowed.


我有什么选择?

  1. 添加带有索引的文件列并使用插入/更新时填充此列的 INSERT/UPDATE 触发器?
  2. 创建一个“计算”该列的视图?无法添加索引,还是会很慢

到目前为止,只有选项 1. 有一些优点,但我不喜欢触发器,也许还有其他选项?

【问题讨论】:

  • 是的,您希望在插入期间解析 JSON,并将这些键/值对存储到正确的架构和索引表中,以提高查询效率。
  • 我希望这是一个错字,我们曾经看到过"Value"(单数)和一次"Values"(复数)。两者都命名一个值数组...
  • 是的,一个错字,已修复,在我的情况下,它是单数 Value,带有值数组。
  • @Lars,好吧......所以你必须在我的回答中纠正这个;-)
  • @Jon 数字。无法更改 JSON 以适应计算列,即将 Files 数组移动到 Input.Data.Files 属性,必须使用触发器。

标签: json tsql sql-server-2017


【解决方案1】:

你可以试试这个:

注意:我已将2 添加到text2 以填充您的过滤器。我将两者都命名为复数"Values"

DECLARE @mockupTable TABLE(ID INT IDENTITY, DataProperties NVARCHAR(MAX));
INSERT INTO @mockupTable VALUES
(N'{
 "Input": {
   "OrderId": "abc",
   "Data": [
     {
       "Key": "Files",
       "Values": [
         "test2.txt",
         "whatever.jpg"
       ]
     },
     {
       "Key": "Other",
       "Values": [
         "a"
       ]
     }
   ]
 }
}');

查询

SELECT TOP 10 *
FROM @mockupTable t 
CROSS APPLY OPENJSON(t.DataProperties,'$.Input.Data')
WITH([Key] NVARCHAR(100)
    ,[Values] NVARCHAR(MAX) AS JSON) dat
WHERE dat.[Key] = 'Files'
AND dat.[Values] LIKE '%2%';

主要区别在于WITH子句,它用于以类型化的方式和并排返回对象内部的属性(类似于naked OPENJSON 所有列都带有PIVOT - 但更好)。这避免了 WHERE... 中昂贵的 JSON 方法...

提示:当我们返回带有NVARCHAR(MAX) AS JSONValue 时,我们可以继续使用嵌套数组,并且可能会继续进行如下操作:

SELECT TOP 10 *
FROM @mockupTable t 
CROSS APPLY OPENJSON(t.DataProperties,'$.Input.Data')
WITH([Key] NVARCHAR(100)
    ,[Values] NVARCHAR(MAX) AS JSON) dat
WHERE dat.[Key] = 'Files'
--we read the array again with `OPENJSON`:
AND 'test2.txt' IN(SELECT [Value] FROM OPENJSON(dat.[Values]));

您可以再使用一个CROSS APPLY 来添加数组的值并直接在WHERE 处过滤。

SELECT TOP 10 *
FROM @mockupTable t 
CROSS APPLY OPENJSON(t.DataProperties,'$.Input.Data')
WITH([Key] NVARCHAR(100)
    ,[Values] NVARCHAR(MAX) AS JSON) dat
CROSS APPLY OPENJSON(dat.[Values]) vals
WHERE dat.[Key] = 'Files'
  AND vals.[Value]='test2.txt'

去看看吧……

【讨论】:

  • 第一个例子 - 我需要通过通配符过滤,而不是精确值,而且没有任何索引它仍然很慢。第二个例子 - 仍然很慢,我需要一个索引。全表扫描不是一个选项。
  • 如果您想快速查询它,您需要将 JSON 从列中取出并放入具有键/值或键/值和正确索引的适当模式
  • @Lars,你必须找到最适合你的东西...... LIKE 带有领先的% 无论如何都不会使用和索引。这总是很慢...解析 JSON 是 - 在任何情况下 - 一个昂贵的操作。开发人员做得很好,很艰难......这不会变得更快。
  • 尾随 % 就足够了。无论如何,将使用触发器和一个普通的 varchar 列并按它过滤。感谢您的帮助。
  • @Lars,通过 XML,我们可以使用谓词,类似于/Input/Data[Key="Files]/Value,但这在 json 路径中(还)不可用。 XQuery 得到了更好的支持,希望他们很快会为 JSON 引入更好的查询支持。第一个建议将是您在当前 JSON 级别上可以获得的最佳建议...
猜你喜欢
  • 2021-12-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-10
  • 2015-12-21
  • 2021-03-07
  • 1970-01-01
相关资源
最近更新 更多