How to parse Json in SQL - sql

I try to parse JSON with the function OPENJSON with a CROSS APLY but I have some problems.
My Json is like this:
[
{
"Communications":[
{
"ID_Communication":null,
"CommunicationType":"HOME",
"CommunicationValue":"0602060206",
"Priority":0,
"Disabled":false
},
{
"ID_Communication":null,
"CommunicationType":"MOBILE",
"CommunicationValue":"0602060306",
"Priority":0,
"Disabled":false
},
{
"ID_Communication":null,
"CommunicationType":"EMAIL",
"CommunicationValue":"MONEMAIL#EMAIL.FR",
"Priority":0,
"Disabled":false
}
],
"InternalId":23126,
"ExternalId":"",
"LastUpdateDate":"2020-01-05T12:04:53",
"Type1":{
"Id":1
},
"Type2":{
"Id":2
},
"Type3":null,
"Title":{
"Id":1
},
"LastName":"TOTO",
"FirstName":"TITI",
"OrganizationName":"",
"Sex":"M",
"BirthDate":"1959-10-07T00:00:00",
"Adresses":[
{
"ID_Address":null,
"Address1":"1 RUE DE FRANCE",
"Address2":"",
"Address3":"",
"Address4":null,
"ZipCode":"94500",
"CityName":"MA VILLE",
"Country":{
"Id":"FR"
},
"Type":null,
"State":null,
"Priority":0,
"ScopeId":0
}
],
"Language":{
"Id":"FR"
},
"Comment":"",
"PassportNumber":"",
"IdentityCardNumber":"",
"Nationality":null,
"SocialGroup":{
"Id":1
},
"OptIns":[
{
"OptInType":"OPT_CLUB",
"OptInLabel":"Optin club",
"OptInValue":0
},
{
"OptInType":"OPT_PART",
"OptInLabel":"Optin partenaires",
"OptInValue":0
}
],
"WebLogin":"MONEMAIL#EMAIL.FR"
}
]
I have try a query like this:
This Query try to get differents informations about this JSON.
declare #JSON_CONTACT nvarchar(MAX)
SELECT #JSON_CONTACT = '[
{
"Communications":[
{
"ID_Communication":null,
"CommunicationType":"HOME",
"CommunicationValue":"0602060206",
"Priority":0,
"Disabled":false
},
{
"ID_Communication":null,
"CommunicationType":"MOBILE",
"CommunicationValue":"0602060306",
"Priority":0,
"Disabled":false
},
{
"ID_Communication":null,
"CommunicationType":"EMAIL",
"CommunicationValue":"MONEMAIL#EMAIL.FR",
"Priority":0,
"Disabled":false
}
],
"InternalId":23126,
"ExternalId":"",
"LastUpdateDate":"2020-01-05T12:04:53",
"Type1":{
"Id":1
},
"Type2":{
"Id":2
},
"Type3":null,
"Title":{
"Id":1
},
"LastName":"TOTO",
"FirstName":"TITI",
"OrganizationName":"",
"Sex":"M",
"BirthDate":"1959-10-07T00:00:00",
"Adresses":[
{
"ID_Address":null,
"Address1":"1 RUE DE FRANCE",
"Address2":"",
"Address3":"",
"Address4":null,
"ZipCode":"94500",
"CityName":"MA VILLE",
"Country":{
"Id":"FR"
},
"Type":null,
"State":null,
"Priority":0,
"ScopeId":0
}
],
"Language":{
"Id":"FR"
},
"Comment":"",
"PassportNumber":"",
"IdentityCardNumber":"",
"Nationality":null,
"SocialGroup":{
"Id":1
},
"OptIns":[
{
"OptInType":"OPT_CLUB",
"OptInLabel":"Optin club",
"OptInValue":0
},
{
"OptInType":"OPT_PART",
"OptInLabel":"Optin partenaires",
"OptInValue":0
}
],
"WebLogin":"MONEMAIL#EMAIL.FR"
}
]'
DROP TABLE TEMP_JSON_RCT_TEST_PARSE
SELECT *
INTO TEMP_JSON_RCT_TEST_PARSE
FROM OPENJSON (#JSON_CONTACT)
WITH (
FIRSTNAME nvarchar(50) '$.FirstName',
LASTNAME nvarchar(50) '$.LastName',
Sex nvarchar(2) '$.Sex',
BirthDate date '$.BirthDate',
Communications nvarchar(max) AS JSON
) AS Communications
CROSS APPLY OPENJSON(Communications)
WITH (
CommunicationType nvarchar(50),
CommunicationValue nvarchar(50),
Adresses nvarchar(max) AS JSON
) AS Adresses
CROSS APPLY OPENJSON(Adresses)
WITH (
Address1 nvarchar(100)
)
I would like my select return:
FIRSTNAME, LASTNAME, SEX, BIRTHDATE, EMAIL, MOBILE, HOME, LASTUPDATEDATE, ADDRESS1, ADRESSE2, ADDRESSE3, ADDRESS4, ZIPCODE, CITYNAME, OPT_CLUB, OPT_PART
VALUES:
MY FIRSTNAME, MYLASTNAME, M, 09/05/1989, MONEMAIL#EMAIL.FR, 0602060306,0602060206, 2020-01-05T12:04:53, 1 RUE DE FRANCE, , , , 94500, MA VILLE, 0, 0
When I execute my query, that return null, I don't understand why.
Can you help about this ?

This seems to return your expected results, but note that this works only if OptIns and Communications JSON arrays has the structure in the question. You need to use OPENJSON() with explicit schema and AS JSON clause for nested JSON arrays:
SELECT
FIRSTNAME, LASTNAME, SEX, BIRTHDATE,
EMAIL, MOBILE, HOME,
LASTUPDATEDATE, ADDRESS1, ADDRESS2, ADDRESS3, ADDRESS4, ZIPCODE, CITYNAME,
OPT_CLUB, OPT_PART
FROM OPENJSON(#JSON_CONTACT, '$') WITH (
FIRSTNAME nvarchar(100) '$.FirstName',
LASTNAME nvarchar(100) '$.LastName',
SEX nvarchar(1) '$.Sex',
BIRTHDATE nvarchar(100) '$.BirthDate',
Communications nvarchar(max) '$.Communications' AS JSON,
Adresses nvarchar(max) '$.Adresses' AS JSON,
LASTUPDATEDATE nvarchar(100) '$.LastUpdateDate',
OptIns nvarchar(max) '$.OptIns' AS JSON
) j1
CROSS APPLY (
SELECT
MAX(CASE WHEN CommunicationType = 'EMAIL' THEN CommunicationValue END) AS EMAIL,
MAX(CASE WHEN CommunicationType = 'MOBILE' THEN CommunicationValue END) AS MOBILE,
MAX(CASE WHEN CommunicationType = 'HOME' THEN CommunicationValue END) AS HOME
FROM OPENJSON(j1.Communications) WITH (
CommunicationType nvarchar(100) '$.CommunicationType',
CommunicationValue nvarchar(100) '$.CommunicationValue'
)
) j2
CROSS APPLY OPENJSON(j1.Adresses) WITH (
ADDRESS1 nvarchar(100) '$.Address1',
ADDRESS2 nvarchar(100) '$.Address2',
ADDRESS3 nvarchar(100) '$.Address3',
ADDRESS4 nvarchar(100) '$.Address4',
ZIPCODE nvarchar(100) '$.ZipCode',
CITYNAME nvarchar(100) '$.CityName'
) j3
CROSS APPLY (
SELECT
MAX(CASE WHEN OptInType = 'OPT_CLUB' THEN OptInValue END) AS OPT_CLUB,
MAX(CASE WHEN OptInType = 'OPT_PART' THEN OptInValue END) AS OPT_PART
FROM OPENJSON(j1.OptIns) WITH (
OptInType nvarchar(100) '$.OptInType',
OptInValue int '$.OptInValue'
)
) j4
Result (with the JOSN in the question):
FIRSTNAME LASTNAME SEX BIRTHDATE EMAIL MOBILE HOME LASTUPDATEDATE ADDRESS1 ADDRESS2 ADDRESS3 ADDRESS4 ZIPCODE CITYNAME OPT_CLUB OPT_PART
TITI TOTO M 1959-10-07T00:00:00 MONEMAIL#EMAIL.FR 0602060306 0602060206 2020-01-05T12:04:53 1 RUE DE FRANCE 94500 MA VILLE 0 0

Address is part of main entity
SELECT *
FROM OPENJSON (#JSON_CONTACT)
WITH (
FIRSTNAME nvarchar(50) '$.FirstName',
LASTNAME nvarchar(50) '$.LastName',
Sex nvarchar(2) '$.Sex',
BirthDate date '$.BirthDate',
Communications nvarchar(max) AS JSON,
Adresses nvarchar(max) AS JSON
) AS Communications
CROSS APPLY OPENJSON(Communications)
WITH (
CommunicationType nvarchar(50),
CommunicationValue nvarchar(50)
) AS Adresses
CROSS APPLY OPENJSON(Adresses)
WITH (
Address1 nvarchar(100)
)

Related

OPENJSON does not detect duplicated Value

I need to import and parse some sales records into the database.
Some customers have more than one product, therefore there are several orders with different product but with the same shipping address in the JSON Array.
Problem arise when I try to save the address in Addresses Table:
Clearly I need just one record for each Address, and to achieve this I calculate an Hash of the address fields and I compare it with the Hashes already present in the table:
It seems (well, I'm sure) that the query checks only the first time if the Hash is already present, and if it is not present, it add as many rows as the orders count:
I can imagine this is the standard behavior of OPENJSON function since the parsing of the JSON payload seems to be made by an inner loop, therefore I think I have to use a different approach.... but I have no Idea what to use.
here the JSON payload
declare #json nvarchar(max)=N'[
{
"id": 21660,
"currency": "USD",
"total": "15.00",
"shipping": {
"first_name": "Charles",
"last_name": "Leuschke",
"address_1": "3121 W Olive Ave",
"city": "Burbank",
"state": "CA",
"postcode": "91505",
"country": "US"
},
"line_items": [
{
"id": 1052
}
]
},
{
"id": 21659,
"currency": "USD",
"total": "38.00",
"shipping": {
"first_name": "Charles",
"last_name": "Leuschke",
"address_1": "3121 W Olive Ave",
"city": "Burbank",
"state": "CA",
"postcode": "91505",
"country": "US"
},
"line_items": [
{
"id": 1050
}
]
},
{
"id": 21658,
"currency": "USD",
"total": "38.00",
"shipping": {
"first_name": "Charles",
"last_name": "Leuschke",
"address_1": "3121 W Olive Ave",
"city": "Burbank",
"state": "CA",
"postcode": "91505",
"country": "US"
},
"line_items": [
{
"id": 1048
}
]
}
]'
and the (simplified) query
Insert Into #Addresses
(
orderId,
fullName,
addressLine1,
city,
stateOrProvince,
postalCode,
countryCode,
addressCode
)
SELECT
o.orderId,
concat(s.firstName,' ',s.lastName),
s.addressLine1,
s.city,
s.stateOrProvince,
s.postalCode,
s.countryCode,
convert(nvarchar(64),hashbytes('SHA1',concat(s.firstName, ' ', s.lastName, s.addressLine1, s.city, s.stateOrProvince, s.postalCode, s.countryCode)),2)
FROM OPENJSON(#json)
WITH (
orderId nvarchar(64) '$.id',
shipping nvarchar(max) '$.shipping' AS JSON
) o
CROSS APPLY OPENJSON(shipping)
WITH (
firstName nvarchar(128) '$.first_name',
lastName nvarchar(128) '$.last_name',
addressLine1 nvarchar(128) '$.address_1',
city nvarchar(128) '$.city',
stateOrProvince nvarchar(64) '$.state',
postalCode nvarchar(64) '$.postcode',
countryCode nvarchar(4) '$.country'
) s
left join #Addresses a on a.addressCode=convert(nvarchar(64),hashbytes('SHA1',concat(s.firstName,' ', s.lastName, s.addressLine1, s.city, s.stateOrProvince, s.postalCode, s.countryCode)),2)
where a.addressCode is null
I also prepared a sqlfiddle, where you can see that it returns 3 rows while target is to get just one
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=3031a99e3cd24f6bf383c0de29cf19a1
Just use the standard ROW_NUMBER() deduplication method e.g.
WITH cte AS (
SELECT
o.orderId,
concat(s.firstName,' ',s.lastName) fullName,
s.addressLine1,
s.city,
s.stateOrProvince,
s.postalCode,
s.countryCode,
h.addressCode,
ROW_NUMBER() OVER (PARTITION BY h.addressCode ORDER BY o.OrderId ASC) rn
FROM OPENJSON(#json)
WITH (
orderId nvarchar(64) '$.id',
shipping nvarchar(max) '$.shipping' AS JSON
) o
CROSS APPLY OPENJSON(shipping)
WITH (
firstName nvarchar(128) '$.first_name',
lastName nvarchar(128) '$.last_name',
addressLine1 nvarchar(128) '$.address_1',
city nvarchar(128) '$.city',
stateOrProvince nvarchar(64) '$.state',
postalCode nvarchar(64) '$.postcode',
countryCode nvarchar(4) '$.country'
) s
CROSS APPLY (
VALUES (CONVERT(nvarchar(64), HASHBYTES('SHA1', CONCAT(s.firstName ,' ', s.lastName, s.addressLine1, s.city, s.stateOrProvince, s.postalCode, s.countryCode)),2))
) h (addressCode)
LEFT JOIN #Addresses a ON a.addressCode = h.AddressCode
WHERE a.addressCode IS NULL
)
INSERT INTO #Addresses
(
orderId,
fullName,
addressLine1,
city,
stateOrProvince,
postalCode,
countryCode,
addressCode
)
SELECT orderId,
fullName,
addressLine1,
city,
stateOrProvince,
postalCode,
countryCode,
addressCode
FROM cte
WHERE rn = 1;
Note: If you use CROSS APPLY to calculate the hashcode one avoids calculating it multiple times.

SQL to JSON - Grouping Results into JSON Array combine columns

In SQL Server I have a table with columns (a, b, c, name1, address1, phonenumber1, name2, address2, phonenumber2). I want to generate JSON for a row like:
{
"a" : value_column_a,
"b" : value_column_b,
"c" : value_column_c,
"contacts": [
{
"name" : name1,
"phone_num": phonenumber1,
"address": address1
},
{
"name" : name2,
"phone_num": phonenumber2,
"address": address2
}
]
}
Please, help me! I took some hours, but I could not solved it.
Embedding arrays of objects into JSON on Microsoft SQL Server is achieved by using json_query() over a subquery of the nested data, such as the following...
/*
* Data setup...
*/
create table dbo.Thingiebob (
[a] varchar(14),
[b] varchar(14),
[c] varchar(14),
[name1] varchar(14),
[address1] varchar(14),
[phonenumber1] varchar(14),
[name2] varchar(14),
[address2] varchar(14),
[phonenumber2] varchar(14)
);
insert dbo.Thingiebob ([a], [b], [c], [name1], [address1], [phonenumber1], [name2], [address2], [phonenumber2])
values
('value_column_a', 'value_column_b', 'value_column_c', 'name1', 'address1', 'phonenumber1', 'name2', 'address2', 'phonenumber2');
/*
* Example query...
*/
select
[a],
[b],
[c],
json_query((
select *
from (
select name1, phonenumber1, address1
union
select name2, phonenumber2, address2
) unions ([name], [phone_num], [address])
for json path
), '$') as [contacts]
from dbo.Thingiebob
for json path;
Which yields the following output...
[
{
"a": "value_column_a",
"b": "value_column_b",
"c": "value_column_c",
"contacts": [
{
"name": "name1",
"phone_num": "phonenumber1",
"address": "address1"
},
{
"name": "name2",
"phone_num": "phonenumber2",
"address": "address2"
}
]
}
]

Inserting into sql server tables (2016) from json

I have some json like this:
SET #json =N'[
{ "id" : 2,"name": "John", "surname": "Smith", "lastname":"", "age": 25 },
]'
I need to insert into table with a condition like IF LASTNAME IS EMPTY IN JSON THEN INSERT SURNAME INTO LASTNAME ELSE INSERT LASTNAME INTO LASTNAME
insert into mytable (id,firstname,lastname,age)
select id,name,case statement,age from openjson
WITH (id int 'strict $.id',name nvarchar(100) '$.name',case statement, age int '$.age');
Hi i think this query can respond :
DECLARE #json varchar(MAX)
SET #json =N'[
{ "id" : 2,"name": "John", "surname": "Smith", "lastname":"", "age": 25 },
{ "id" : 2,"name": "John", "surname": "Smith", "lastname":"TT", "age": 25 },
{ "id" : 2,"name": "John", "surname": "Smith", "lastname":"TEST", "age": 25 }
]'
select id,name,case when lastname = '' then surname else lastname end as lastnameup, age from openjson(#json)
WITH (id int 'strict $.id',name nvarchar(100) '$.name', surname nvarchar(100) '$.surname', lastname nvarchar(100) '$.lastname', age int '$.age');
EDIT for surname propose thanks to #Shnugo

How do I get data from JSON Array MSSQL

If I do this I can get the first value in the array. But how can I get all of the values in the list ?
SELECT
JSON_VALUE('{"Distributor": [5030, 4000, 1231]}', '$.Distributor[0]') AS result;
SELECT value
FROM OPENJSON('{"Distributor": [5030, 4000, 1231]}','$.Distributor')
'$.Distributor' is the 'start path' to start looking for an array
You can take look at this article:
https://learn.microsoft.com/fr-fr/sql/relational-databases/json/json-data-sql-server?view=sql-server-2017
Using function Openjson and Outer apply
Example provided:
DECLARE #json NVARCHAR(MAX)
SET #json =
N'[
{ "id" : 2,"info": { "name": "John", "surname": "Smith" }, "age": 25 },
{ "id" : 5,"info": { "name": "Jane", "surname": "Smith", "skills": ["SQL", "C#", "Azure"] }, "dob": "2005-11-04T12:00:00" }
]'
SELECT *
FROM OPENJSON(#json)
WITH (id int 'strict $.id',
firstName nvarchar(50) '$.info.name', lastName nvarchar(50) '$.info.surname',
age int, dateOfBirth datetime2 '$.dob',
skills nvarchar(max) '$.info.skills' as json)
outer apply openjson( skills )
with ( skill nvarchar(8) '$' )
EDIT Code with provided json :
DECLARE #json NVARCHAR(MAX)
SET #json = N'{"Distributor": [5030, 4000, 1231]}'
SELECT Distributor
FROM OPENJSON(#json)
WITH (Distributors nvarchar(max) '$.Distributor' as json)
outer apply openjson( Distributors )
with ( Distributor int '$' )
RESULT :
Distributor
5030
4000
1231

Parsing JSON in SQL Server

Referring to the example provided by Microsoft:
DECLARE #json NVARCHAR(MAX)
SET #json =
N'[
{ "id" : 2,"info": { "name": "John", "surname": "Smith" }, "age": 25 },
{ "id" : 5,"info": { "name": "Jane", "surname": "Smith" }, "dob": "2005-11-04T12:00:00" }
]'
SELECT *
FROM OPENJSON(#json)
WITH (id int 'strict $.id',
firstName nvarchar(50) '$.info.name', lastName nvarchar(50) '$.info.surname',
age int, dateOfBirth datetime2 '$.dob')
when the JSON data has an array inside, for example:
DECLARE #json NVARCHAR(MAX)
SET #json =
N'[
{ "id" : 2,"info": { "name": "John", "surname": "Smith" },
"Phones": ["123","345","678"] // like here
}
]'
Is there any way to join the array on the parsed data to get something like this:
Id First Name Last Name Phone
2 John Smith 123
2 John Smith 345
2 John Smith 678
OUTER APPLY is your friend here. Let's mix up both kinds of rows for good measure.
DECLARE #json NVARCHAR(MAX)
SET #json = N'
[
{
"id": 2,
"info": {
"name": "John",
"surname": "Smith"
},
"age": 25,
"Phones": [
"123",
"345",
"678"
]
},
{
"id": 5,
"info": {
"name": "Jane",
"surname": "Smith"
},
"dob": "2005-11-04T12:00:00"
}
]'
SELECT id, [name], [surname], age, dateOfBirth, number
FROM (
SELECT *
FROM OPENJSON(#json)
WITH (
id INT 'strict $.id',
[name] NVARCHAR(50) '$.info.name',
[surname] NVARCHAR(50) '$.info.surname',
age INT,
dateOfBirth DATETIME2 '$.dob',
Phones NVARCHAR(MAX) AS JSON
)
) AS people
OUTER APPLY OPENJSON(Phones)
WITH (
number NVARCHAR(50) '$'
)
Result:
+----+------+---------+------+-----------------------------+--------+
| id | name | surname | age | dateOfBirth | number |
+----+------+---------+------+-----------------------------+--------+
| 2 | John | Smith | 25 | NULL | 123 |
| 2 | John | Smith | 25 | NULL | 345 |
| 2 | John | Smith | 25 | NULL | 678 |
| 5 | Jane | Smith | NULL | 2005-11-04 12:00:00.0000000 | NULL |
+----+------+---------+------+-----------------------------+--------+
Well, you can always do something like this:
SELECT ID, FirstName, LastName, value
FROM OPENJSON(#json)
WITH(ID int '$.id',
FirstName nvarchar(50) '$.info.name',
LastName nvarchar(50) '$.info.surname',
Phones nvarchar(max) '$.Phones' AS Json)
CROSS APPLY OPENJSON(Phones)
Hope this helps.
You will have to do it in two passes:
Extract for each person data and leaving array of phone numbers as JSON
Use CROSS/OUTER APPLY to parse array of phone numbers for each person and join result together
Something like this:
DECLARE #json NVARCHAR(MAX)
SET #json =
N'[
{ "id" : 2,"info": { "name": "John", "surname": "Smith" }, "Phones": ["123","345","678"] },
{ "id" : 3,"info": { "name": "Jane", "surname": "Smith" }, "Phones": ["321","543"] }
]';
WITH CTE AS (
SELECT id, firstName, lastName, phones
FROM OPENJSON(#json)
WITH (
id INT 'strict $.id',
firstName NVARCHAR(50) '$.info.name',
lastName NVARCHAR(50) '$.info.surname',
phones NVARCHAR(MAX) '$.Phones' AS JSON
)
)
SELECT c.id, c.firstName, c.lastName, p.value as phone
FROM CTE c
CROSS APPLY OPENJSON(c.phones) p
You can use C# library
https://www.dotnet4techies.com/2018/07/convert-json-to-sql-format-using-csharp.html
It is a powerful tool and it maintains sql relationships from your Json if it has nested levels.