Rawdon Hume
Rawdon Hume

Reputation: 145

MS SQL json query/where clause nested array items

I have json data that i can query on using CROSS APPLY OPENJSON( which gets slow once you start adding multiple cross applies or once your json document get too large. So i wanted to add an index on the data im trying to filter on, but i cant get the syntax on nested array items to work with out using a cross apply. As such i cant create an index as you cant use a cross apply when making an index. According to the MS docs i should just be able to do

JSON_query(my_column, $.parentItem.nestedItemsArray1.nestedItemsArray2)

I should be able to get all the values of the nested, array items to then query on and improve performance by adding an index, something like this

ALTER TABLE mytable 
ADD vdata AS JSON_query(my_column, 
  $.parentItem.nestedItemsArray1.nestedItemsArray2')  
  
CREATE INDEX idx_json_my_column ON mytable(vdata)

but the above $.array.arrayitems syntax doesn't work ? On a side note, I cant help but think in relational terms where normally in Sql you would index a column of data like so

col
---
 1| 
 2|
 3|

But json data seem to get flattened so when i use JSON_QUERY as per MS example i get "1,2,3" " I assume i want to incdex an array of values rather than a flattened version unless the index will return the inner data of the fattened data ?

my plug and play working example

declare  @mydata table (    
  ID int NOT NULL,  
  jsondata varchar(max) NOT NULL  
)

INSERT INTO @mydata (id, jsondata)
    VALUES (789, '{ "Id": "12345", "FinanceProductResults": [ { "Term": 12, "AnnualMileage": 5000, "Deposits": 0, "ProductResults": [] }, { "Term": 18, "AnnualMileage": 30000, "Deposits": 15000, "ProductResults": [] }, { "Term": 24, "AnnualMileage": 5000, "Deposits": 0, "ProductResults": [ { "Key": "HP", "Payment": 460.28 } ] }, { "Term": 24, "AnnualMileage": 10000, "Deposits": 0, "ProductResults": [ { "Key": "HP", "Payment": 500.32 } ] }]}')

SELECT 
    j_Id
    ,JSON_query (c.value, '$.Term') as Term
    ,JSON_Value (c.value, '$.AnnualMileage') as AnnualMileage
    ,JSON_Value (c.value, '$.Deposits') as Deposits     
    ,JSON_Value (p.value, '$.Key') as [Key]
    ,JSON_Value (p.value, '$.Payment') as Payment
    --,c.value
FROM @mydata  f 
    CROSS APPLY OPENJSON(f.jsondata)
        WITH (j_Id nvarchar(100) '$.Id')
    CROSS APPLY OPENJSON(f.jsondata, '$.FinanceProductResults') AS c
    CROSS APPLY OPENJSON(c.value, '$."ProductResults"') AS p
where 
    ID = 789 
    AND JSON_Value (p.value, '$.Payment') = '460.28'

I'm using these MS docs to guide me :

How to create an index

How to get data

Update

I was able to improve performance slightly using the "with" method

SELECT 
    j_Id,
    FinanceDetails.Term,
    FinanceDetails.AnnualMileage,
    FinanceDetails.Deposits,
    Payments.Payment
FROM @mydata  f 
    CROSS APPLY OPENJSON(f.jsondata)
        WITH (j_Id nvarchar(100) '$.Id')
    OUTER APPLY OPENJSON (f.jsondata, '$.FinanceProductResults' )  
            WITH (   
                Term            INT             '$.Term',  
                AnnualMileage   INT             '$.AnnualMileage',  
                Deposits        INT             '$.Deposits',  
                ProductResults  NVARCHAR(MAX)   '$.ProductResults'  AS JSON  
            ) AS FinanceDetails
        OUTER APPLY OPENJSON(ProductResults,  '$') 
            WITH (   
                Payment DECIMAL(19, 4)          '$.Payment' 
            ) AS Payments
WHERE   
    Payments.Payment = 460.28

but i still like to add an index on the sub array data to aid in improving performance ?

Upvotes: 4

Views: 5551

Answers (1)

Jovan MSFT
Jovan MSFT

Reputation: 14600

Currently, you cannot index nested properties. Is Full-text search possible option? You might create FTS on JSON column and add predicate:

WHERE ....
AND CONTAINS( jsondata, 'NEAR(('Payments,460),1)')

Since JSON is text, this predicate will filter out all records that don't have something like "Payment" and 460 near to each other (this will identify key:value pairs), and you can apply CROSS APPLY on the reduced set of rows.

Upvotes: 2

Related Questions