Back to articles list
- 5 minutes read

Support, Functionality, and Limitations of JSON in SQL Server 2016

As JSON continues to increase in popularity, support from third-party products is burgeoning as well. According to the Microsoft team, JSON support was one of the most requested features on Microsoft connect prior to its official announcement. While some JSON functionality is available in SQL Server 2016, significant limitations may hamper development and storage efforts.

The Basics of JSON

JSON is a language-independent format to store objects in attribute-value pairs. Due to its compactness and flexibility, it is increasingly replacing XML. Its simplicity makes it superior to XML as a data-interchange format; although JSON lacks the structured overhead of XML, that feature is unimportant for data interchange.

Here’s a simple JSON example, taken from JSON.org. It describes the representation of a menu, using attribute:value pairs to describe the underlying data:

{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}

Menu is the schema at the root of the definition – typically, this entire representation would be referred to as the menu schema. Other schemas, such as menuitem, are considered nested schemas.

JSON in SQL Server

Unlike XML support – in which SQL Server uses a specific datatype – JSON data is stored as VARCHAR. This implementation has been widely derided by the SQL Server community, as it greatly limits or eliminates the ability to index and rapidly query JSON data. The same limitations and performance degradations of string operations will be present. For simple storage and retrieval, however, the current JSON support is sufficient.

Converting Relational Data to JSON

SQL Server 2016 introduces the FOR JSON clause, which represents stored data in column order. The simple example below creates a table of employee data, then represents it as a JSON schema:




CREATE TABLE JSONEmployee (EmployeeID INT IDENTITY, EmployeeFirstName Varchar(64), EmployeeLastName 
Varchar(64), EmployeeDOB Date)
GO

INSERT JSONEmployee (EmployeeFirstName, EmployeeLastName, EmployeeDOB)
 VALUES ('Paul', 'Kariya', '1974-10-16'), ('Steve', 'Yzerman', '1904-03-02')
GO

Select EmployeeFirstName, EmployeeLastName, EmployeeDOB
From JSONEmployee
FOR JSON AUTO
GO

Output:

[{"EmployeeFirstName":"Paul","EmployeeLastName":"Kariya","EmployeeDOB":"1974-10-16"},
{"EmployeeFirstName":"Steve","EmployeeLastName":"Yzerman","EmployeeDOB":"1904-03-02"}]

A slightly more complex example allows for multiple values in a nested schema, DEPT (Department):

CREATE TABLE EmployeeDepartment (EmployeeDepartmentID INT IDENTITY, EmployeeID INT, 
EmployeeDepartment Varchar(32))
GO

INSERT EmployeeDepartment (EmployeeID, EmployeeDepartment)
VALUES (1, 'Ducks'), (1, 'Blues'), (2, 'Red Wings')
GO


Select EmployeeFirstName, EmployeeLastName, EmployeeDOB,
(Select EmployeeDepartment from EmployeeDepartment DEPT Where DEPT.EmployeeID = EMP.EmployeeID
FOR JSON AUTO) DEPT
From JSONEmployee EMP
FOR JSON AUTO
GO

Output:

[{"EmployeeFirstName":"Paul","EmployeeLastName":"Kariya","EmployeeDOB":"1974-10-16",
"DEPT":[{"EmployeeDepartment":"Ducks"},{"EmployeeDepartment":"Blues"}]},
{"EmployeeFirstName":"Steve","EmployeeLastName":"Yzerman","EmployeeDOB":"1904-03-02",
"DEPT":[{"EmployeeDepartment":"Red Wings"}]}]

In the output, Paul has two assigned departments.

The above example uses FOR JSON AUTO to format the results based on the structure of the select statement. FOR JSON PATH allows you to have finer control over the output format. You can define the JSON structure in the query by defining a column alias with a dot- separated (.) value. In the next code block, EmployeeDepartment is formatted at the root:

Select EmployeeFirstName as 'Employee.FirstName', 
EmployeeLastName as 'Employee.LastName', 
EmployeeDOB as 'Employee.DOB',
(Select EmployeeDepartment from EmployeeDepartment DEPT Where DEPT.EmployeeID = EMP.EmployeeID
FOR JSON AUTO) as as 'Employee.DEPT'
From JSONEmployee EMP
FOR JSON PATH, root ('EmployeeDepartment')
GO

{"EmployeeDepartment":
[{"Employee" : 
      { "FirstName":"Paul",
        "LastName":"Kariya",
         "DOB":"1974-10-16",
         "DEPT":[{"EmployeeDepartment":"Ducks"},{"EmployeeDepartment":"Blues"}]
       }
   },
 {"Employee" : 
       { "FirstName":"Steve",
         "LastName":"Yzerman",
         "DOB":"1904-03-02",
         "DEPT":[{"EmployeeDepartment":"Red Wings"}]
        }
   }
]}

FOR JSON PATH can be used to display JSON text in various ways when AUTO is insufficient.

Parsing JSON Data in SQL Server

SQL Server 2016 introduces JSON_QUERY and JSON_VALUE, two functions that return values from JSON text. JSON_QUERY is used to return objects or arrays, while JSON_VALUE returns a scalar value.

DECLARE @MyJSON NVARCHAR(4000) = 
N'{
    "parentschema":{  
      "type":1,
      "address":{  
        "town":"Louisville",
        "county":"Boulder",
        "country":"USA"
      },
      "tags":["Snow", "Breweries"]
   },
   "type":"Location"
}'


SELECT JSON_QUERY(@MyJSON, '$.parentschema.address') AS Address
SELECT JSON_VALUE(@MyJSON, '$.parentschema.address') AS Address

SELECT JSON_QUERY(@MyJSON, '$.parentschema.type') AS Address
SELECT JSON_VALUE(@MyJSON, '$.parentschema.type') AS Address

Output:

{  
        "town":"Louisville",
        "county":"Boulder",
        "country":"USA"
      }
NULL
NULL
1

In the above example, the address contains multiple values; JSON_QUERY will return the array while JSON_VALUE returns a NULL. Conversely, JSON_VALUE returns a 1 for the scalar ‘type’, while JSON_QUERY returns a NULL.

Both JSON_QUERY and JSON_VALUE can be configured to return an error rather than a NULL by specifying the strict keyword:

SELECT JSON_VALUE(@MyJSON, 'strict $.parentschema.address') AS Address

Output:

Msg 13608, Level 16, State 1, Line 76
Property cannot be found in specified path.

Testing JSON Data

The SQL function ISJSON returns a “1” for valid JSON data, or a “0” if the string is not JSON data.

DECLARE @jsonInfo VARCHAR(MAX)
SET @jsonInfo = N'{
    "info":{  
      "type":1,
      "address":{  
        "town":"Bristol",
        "county":"Avon",
        "country":"England"
      },
      "tags":["Sport", "Water polo"]
   },
   "type":"Basic"
}'


SELECT ISJSON(@jsonInfo)

Set @JsonInfo = 'This does not look very JSON'

SELECT ISJSON(@jsonInfo)

Output:

1
0

Converting JSON Data to a Relational Format

Arguably the most useful JSON function, OPENJSON converts JSON text to a relational format for storage. The key, value, and datatype are returned from the function:

DECLARE @MyJSON NVARCHAR(4000) = 
N'{
    "parentschema":{  
      "type":1,
      "address":{  
        "town":"Louisville",
        "county":"Boulder",
        "country":"USA"
      },
      "tags":["Snow", "Breweries"]
   },
   "LocationType":"Location",
   "WeatherType":"Cold"
}'

Select * from  OPENJSON (@MyJSON)

Converting JSON Data to a Relational Format

This example uses a default schema, but an explicit schema with required datatypes can also be expressed.

JSON and SQL Server: What Will the Future Bring?

While there are some helpful functions for manipulating JSON text in SQL Server 2016, the decision to implement JSON over VARCHAR may limit its overall usefulness. If there is significant demand for increased JSON support, Microsoft may elect to implement a JSON datatype in the near future. For now, SQL Server 2016 has enough compatibility to support basic storage and manipulation.

go to top