在对象数组内的特定JSON值上创建索引

时间:2018-09-17 18:37:32

标签: sql-server tsql

因此,假设我在具有某种结构的表中有一个varchar列:

{
   "Response":{
      "DataArray":[
         {
            "Type":"Address",
            "Value":"123 Fake St"
         },
         {
            "Type":"Name",
            "Value":"John Doe"
         }
      ]
   }
}

我想在“ DataArray”数组元素的“ Value”字段上创建一个持久的计算列,该字段包含一个等于“ Name”的Type字段。 (我希望我能正确解释。基本上,我想在该结构上索引人物姓名。)

问题在于,与其他json对象不同,我无法直接使用JSON_VALUE函数来提取所述值。我不知道是否可以做到这一点,我一直在摸索JSON_QUERY,但到目前为止我还不知道该怎么办。

任何想法和帮助表示赞赏。谢谢!

3 个答案:

答案 0 :(得分:5)

您可以使用以下功能来实现它:

CREATE FUNCTION dbo.my_func(@s NVARCHAR(MAX))
RETURNS NVARCHAR(100)
WITH SCHEMABINDING
AS
BEGIN
   DECLARE @r NVARCHAR(100);

   SELECT @r = Value 
   FROM OPENJSON(@s,'$.Response.DataArray')  
   WITH ([Type] NVARCHAR(100) '$.Type', [Value] NVARCHAR(100) '$.Value')
   WHERE [Type] = 'Name';

   RETURN @r;
END;

定义表:

CREATE TABLE tab(
  val NVARCHAR(MAX) CHECK (ISJSON(val) = 1),
  col1 AS dbo.my_func(val) PERSISTED        -- calculated column
);

样本数据:

INSERT INTO tab(val) VALUES (N'{
   "Response":{
      "DataArray":[
         {
            "Type":"Address",
            "Value":"123 Fake St"
         },
         {
            "Type":"Name",
            "Value":"John Doe"
         }
      ]
   }
}');

CREATE INDEX idx ON tab(col1);   -- creating index on calculated column

SELECT * FROM tab;

db<>fiddle demo

答案 1 :(得分:2)

您可以将计算列与PATINDEX一起使用,并为其编制索引:

std::uintptr_t

答案 2 :(得分:1)

您可以使用@Lukasz Szozda发布的标量函数-这是一个很好的解决方案。 但是,计算列中的T-SQL标量UDF的问题在于它们会破坏表所涉及的任何查询的性能。不仅数据修改(插入,更新,删除)会减慢速度,而且查询的任何执行计划也会涉及该表不能利用并行执行计划。 即使在查询中未引用计算列的情况下,也是如此 。甚至索引构建也失去了利用并行执行计划的能力。请注意本文:Another reason why scalar functions in computed columns is a bad idea,作者Erik Darling。

这不是那么漂亮,但是,如果性能很重要,那么它将获得所需的结果,而没有标量UDF的缺点。

CREATE TABLE dbo.jsonStrings
(
  jsonString VARCHAR(8000) NOT NULL, 
  nameTxt AS (
    SUBSTRING(
      SUBSTRING(jsonString,
        CHARINDEX('"Value":"',jsonString,
          CHARINDEX('"Type":"Name",',jsonString,
            CHARINDEX('"DataArray":[',jsonString)+12))+9,8000),1,
      CHARINDEX('"', 
        SUBSTRING(jsonString,
          CHARINDEX('"Value":"',jsonString,
            CHARINDEX('"Type":"Name",',jsonString,
              CHARINDEX('"DataArray":[',jsonString)+12))+9,8000))-1)) PERSISTED
);

INSERT dbo.jsonStrings(jsonString)
VALUES
('{
   "Response":{
      "DataArray":[
         {
            "Type":"Address",
            "Value":"123 Fake St"
         },
         {
            "Type":"Name",
            "Value":"John Doe"
         }
      ]
   }
}');

请注意,这对于您发布的结构非常有效。可能需要根据JSON的功能和外观进行调整。

第二种(也是更好但更复杂)的解决方案是从Lukasz Szozda的标量UDF中获取json路径逻辑,并将其放入CLR中。如果正确编写T-SQL标量UDF,则不会出现T-SQL标量UDF所存在的上述问题。