Create a json based on multiple queries on the same table when join is irrelevant

All we need is an easy explanation of the problem, so here it is.

I need to construct a json based on the results of two different queries on the same table, where a join is irrelevant.

Consider the following example (full demo is here):

CREATE TABLE Trees
(
    [Id] INT,
    [Type] NVARCHAR(100),
    [Height] DECIMAL(2,1)
);

INSERT INTO Trees ([Id], [Type], [Height])
VALUES 
 (1, 'Palm', 5.5), 
 (2, 'Pine', 6.2), 
 (3, 'Apple', 2.5), 
 (4, 'Japanese Cedar', 0.5), 
 (5, 'Spanish Fir', 0.6);

I want to construct the following json:

{
  "highTrees": 
  [
     {
        "id": 1,
        "type": "Palm",
        "height": 5.5
     },
     {
       "id": 1,
        "type": "Pine",
        "height": 6.2
     }
  ],
  "lowTrees": 
  [
     {
        "id": 4,
        "type": "Japanese Cedar",
        "height": 0.5
     },
     {
       "id": 5,
        "type": "Spanish Fir",
        "height": 0.6
     }
  ]
}
  

I tried this:

SELECT 
   Id as 'highTrees.id',
   Type as 'highTrees.type',
   Height as 'highTrees.height'
FROM Trees WHERE [Height] > 5
UNION ALL
SELECT 
   Id as 'lowTrees.id',
   Type as 'lowTrees.type',
   Height as 'lowTrees.height'
FROM Trees WHERE [Height] < 1
FOR JSON PATH;

But obviously this isn’t the way to go, since it gives this:

[
  {
    "highTrees": {
      "id": 1,
      "type": "Palm",
      "height": 5.5
    }
  },
  {
    "highTrees": {
      "id": 2,
      "type": "Pine",
      "height": 6.2
    }
  },
  {
    "highTrees": {
      "id": 4,
      "type": "Japanese Cedar",
      "height": 0.5
    }
  },
  {
    "highTrees": {
      "id": 5,
      "type": "Spanish Fir",
      "height": 0.6
    }
  }
]

How can I achieve the desired result?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

In your expected JSON, highTrees and lowTrees are keys. And keys you typically obtain from columns. Therefore, those should be separate columns in the generating query, rather than separate subsets of rows in the same row set.

Knowing that, you could modify your UNION query like this (live demo):

SELECT
  highTrees = JSON_QUERY(
                (
                  SELECT 
                  Id as id,
                  Type as type,
                  Height as height
                  FROM Trees WHERE [Height] > 5
                  FOR JSON PATH
                )
              ),

  lowTrees  = JSON_QUERY(
                (
                  SELECT 
                  Id as id,
                  Type as type,
                  Height as height
                  FROM Trees WHERE [Height] < 1
                  FOR JSON PATH
                )
              )
FOR JSON
  PATH, WITHOUT_ARRAY_WRAPPER
;

and get (almost) the expected output:

{
  "highTrees": [
    {
      "id": 1,
      "type": "Palm",
      "height": 5.5
    },
    {
      "id": 2,
      "type": "Pine",
      "height": 6.2
    }
  ],
  "lowTrees": [
    {
      "id": 4,
      "type": "Japanese Cedar",
      "height": 0.5
    },
    {
      "id": 5,
      "type": "Spanish Fir",
      "height": 0.6
    }
  ]
}

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply