OPENJSON does not detect duplicated Value - sql

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.

Related

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"
}
]
}
]

insert json to sql table getting null

I have the following json , when i am trying to extract it to sql i get empty.
I need CusP.id, CusP.custldId ,cusfield.id, value
DECLARE #json NVARCHAR(MAX);
SELECT #json = '{
"includ": {
"cusP": {
"542310": {
"id": 542310,
"custldId": 155,
"cusfield": {
"id": 155,
"type": "custfi"
},
"projectId": 17435,
"project": {
"id": 17435,
"type": "projects"
},
"value": "META DATA",
"createdAt": "2022-01-16T05:11:20Z",
"createdBy": 222222
},
"21000": {
"id": 21000,
"custldId": 426,
"cusfield": {
"id": 426,
"type": "custfi"
},
"projectId": 786044,
"project": {
"id": 786044,
"type": "projects"
},
"value": "delta55",
"createdAt": "2022-01-17T10:03:07Z",
"createdBy": 333333
}
}
}
}'
This is what i am trying:
SELECT
D.cusPid,
d.[value],
c.cusfieldid,
cd.projectId
FROM OPENJSON(#json, '$.includ.cusP')
WITH (
cusPid NVARCHAR(max) '$.id',
[value] NVARCHAR(max) '$.value'
) D
CROSS APPLY OPENJSON(#json, '$.includ.cusP.custfi')
WITH (
cusfieldid VARCHAR(100) '$.id'
) C
CROSS APPLY OPENJSON(#json, '$.includ.cusP.project')
WITH (
projectId VARCHAR(100) '$.id'
) Cd;
that is the result i expect
cusPid
value
cusfieldid
projectId
542310
META DATA
155
17435
21000
delta55
426
786044
The problem is that the ID is also itself used as the key for a sub-property and OPENJSON does not allow variable paths (beyond arrays), so you need an extra level:
SELECT P.id AS cuspID, P.[value], P.cusfieldid, [projectId]
FROM OPENJSON(#json, '$.includ.cusP') J
CROSS APPLY OPENJSON(J.[value]) WITH (
id INT,
[value] NVARCHAR(MAX),
[projectId] INT,
cusfieldid INT '$.cusfield.id'
) P

Generate nested JSON from stored procedure

I have sample data in a SQL Server table in the following format
CREATE TABLE #tempA
(
HomeId int IDENTITY PRIMARY KEY,
City nvarchar(20),
State nchar(2),
Email VARCHAR(50)
);
INSERT INTO #tempA (City, State, Email)
VALUES ('Cleveland', 'OH', 'sd#aol.com')
INSERT INTO #tempA (City, State, Email)
VALUES ('Malibu', 'CA', 'sd#aol.com')
INSERT INTO #tempA (City, State, Email)
VALUES ('Atlanta', 'GA', 'ploll#aol.com')
SELECT * FROM #tempA
I need a JSON output returned by a stored procedure in the following format, I am trying to group it by email field, I tried using JSON AUTO but not able to achieve in the following format ? Any tips?
[
{
"Email": "sd#aol.com",
"Tasks": [
{
"City": "Cleveland",
"State": "OH"
},
{
"City": "Malibu",
"State": "CA"
}
]
},
{
"Email": "ploll#aol.com",
"Tasks": [
{
"City": "Atlanta",
"State": "GA"
}
]
}
]
One method would be to use a subquery for the State and Email values and group on the email column in the outer query:
SELECT A.Email,
(SELECT B.City,
B.State
FROM #tempA B
WHERE A.Email = B.Email
ORDER BY B.City ASC
FOR JSON AUTO) AS Tasks
FROM #tempA A
GROUP BY A.Email
ORDER BY A.Email DESC
FOR JSON AUTO;
Which gives:
[
{
"Email": "sd#aol.com",
"Tasks": [
{
"City": "Cleveland",
"State": "OH"
},
{
"City": "Malibu",
"State": "CA"
}
]
},
{
"Email": "ploll#aol.com",
"Tasks": [
{
"City": "Atlanta",
"State": "GA"
}
]
}
]

SQL query OpenJson with deep nested arrays loop

I know there a lot of similar questions and answers here. I've read most of them but I'm unable to query nested arrays in a JSON structure. I'm lost in the CROSS APPLY's.
I'm actually querying a web API but for the sake of my question I've put it in a variable.
I'm trying to insert the ID (3519) and all the info of "worker_contracts" into a table.
declare #json nvarchar(max)
set #json = '
{
"data": [
{
"id": "3519",
"type": "affiliate_workers",
"attributes": {
"title": "mr",
"first_name": "John",
"last_name": "Doe",
"telephone": "+32 471 12 34 56",
"worker_contracts": [
{
"start_date": "2020-01-06",
"end_date": null,
"social_secretary_specific_data": {
"affiliate_id": 54,
"worker_details_id": 3378,
"start_date": "2020-01-06",
"end_date": null,
"affiliate_worker_id": 3519,
},
"work_hours_per_week": 25.0,
"comment": "",
"roster_week": [
{
"start_date": "2020-01-13",
"number_hours": 25
},
{
"start_date": "2020-01-06",
"number_hours": 25
}
],
"social_secretary_identifier": "123456"
},
{
"start_date": "2019-09-23",
"end_date": "2020-01-05",
"social_secretary_specific_data": {
"affiliate_id": 54,
"worker_details_id": 3378,
"start_date": "2019-09-23",
"end_date": "2020-01-05",
"affiliate_worker_id": 3519,
},
"work_hours_per_week": 21.0,
"comment": "",
"roster_week": [
{
"start_date": "2019-09-30",
"number_hours": 21
},
{
"start_date": "2019-09-23",
"number_hours": 21
}
],
"social_secretary_identifier": "123456"
}
],
"sodexo_reference": "56789",
"region": "Vlaanderen",
"identity_card_number": "A1122334455",
"can_work_with_animals": false
}
}
]
}
My SQL query only selects the start and end date values. I don't understand how I can add the other nested data.
insert into AffiliateWorkersContract_WRK
select WA.id, wc.*
from OpenJson((CAST(#json as nvarchar(max))),'$.data')
WITH (
id int,
worker_contracts nvarchar(max) as json
)
as WA
cross apply openjson (WA.worker_contracts)
with
(
start_date date '$.start_date',
end_date date '$.end_date',
) as WC
Thank you all for your help in advance.
Found the solution myself. I just had to keep going with the cross apply
insert into AffiliateWorkersContract_WRK
select WA.id, wawc.start_date, wawc.end_date, wawc.signing_date, wawc.contract_variability,
wawc.vehicle_type, wawc.addendum, wawc.work_hours_per_week,wawc.comment, wawc.social_secretary_identifier,
wssd.affiliate_id, wssd.worker_details_id, wssd.created_at, wssd.updated_at, wssd.affiliate_worker_id, wssd.comment, wssd.author_id,
wssd.parent_id, wssd.operating_headquarter_id, wssd.reference_period, wssd.varvar_ref_period_avg_minutes_per_week,
wssd.varvar_min_work_minutes_per_week, wssd.varvar_max_work_minutes_per_week
from OpenJson((CAST(#pootsy_AWWC_request as nvarchar(max))),'$.data')
WITH (
id int,
attributes nvarchar(max) as json
) as WA
cross apply openjson ((CAST(WA.attributes as nvarchar(max))))
WITH (
worker_contracts nvarchar(max) as json
) as WC
cross apply OpenJson ((CAST(WC.worker_contracts as nvarchar(max))))
WITH (
start_date date,
end_date date,
signing_date date,
contract_variability varchar(20),
vehicle_type varchar(20),
addendum varchar(10),
work_hours_per_week decimal(4,2),
comment nvarchar(max),
social_secretary_identifier int,
social_secretary_specific_data nvarchar(max) as json,
roster_week nvarchar(max) as json
) AS WAWC
cross apply OpenJson (WAWC.social_secretary_specific_data)
WITH (
affiliate_id int,
worker_details_id int,
created_at varchar(40),
updated_at varchar(40),
affiliate_worker_id int,
comment nvarchar(max),
author_id int,
parent_id int,
operating_headquarter_id int,
reference_period varchar(10),
varvar_ref_period_avg_minutes_per_week decimal(4,2),
varvar_min_work_minutes_per_week decimal(4,2),
varvar_max_work_minutes_per_week decimal(4,2)
) AS WSSD

SQL Server - How to transform JSON to Relational database

How can we use SQL to Convert a JSON statement into different tables?
For example we have JSON:
{"table1":
{"Name":"table1","Items":
[{"Id":1,"FirstName":"John",
"LastName":"Wen","Country":"UK",
"PostCode":1234,"Status":false,
"Date":"2018-09-18T08:30:32.91",}]},
"table2":
{"Name":"table2","Items":
[{"Id":1,"Name":"leo",
"StudentId":102,"CreatedDate":"2018-09-18","Location":"USA"}]}}
In the relational database, we will get two tables once the JSON is converted
For example, schema 'Table1':
Id FirstName LastName Country PostCode Status Date
1 John Wen UK 1234 false 2018-09-18T08:30:32.91
And the 'Table2' will look like:
Id Name StudentId CreateDate Location
1 Leo 102 2018-9-18 USA
Could anyone please give any advices on it.
You can do this using openjson and json_value functions. Try the following:
Declare #json nvarchar(max),#table1Items nvarchar(max), #table2Items nvarchar(max)
set #json='{
"table1": {
"Name": "table1",
"Items": [{
"Id": 1,
"FirstName": "John",
"LastName": "Wen",
"Country": "UK",
"PostCode": 1234,
"Status": false,
"Date": "2018-09-18T08:30:32.91"
}, {
"Id": 2,
"FirstName": "John1",
"LastName": "Wen1",
"Country": "UK1",
"PostCode": 12341,
"Status": true,
"Date": "2018-09-15T08:30:32.91"
}]
},
"table2": {
"Name": "table2",
"Items": [{
"Id": 1,
"Name": "leo",
"StudentId": 102,
"CreatedDate": "2018-09-18",
"Location": "USA"
}]
}
}'
set #table1Items=(select value from OpenJSON((select value from OpenJSON(#Json) where [key]='table1')) where [key]='Items')
set #table2Items=(select value from OpenJSON((select value from OpenJSON(#Json) where [key]='table2')) where [key]='Items')
--select for table 1
select JSON_VALUE(val,'$.Id') as ID,
JSON_VALUE(val,'$.FirstName') as FirstName,
JSON_VALUE(val,'$.LastName') as LastName,
JSON_VALUE(val,'$.Country') as Country,
JSON_VALUE(val,'$.PostCode') as PostCode,
JSON_VALUE(val,'$.Status') as Status,
JSON_VALUE(val,'$.Date') as Date
from
(
select value as val from openJSON(#table1Items)
) AS Table1JSON
--select for table 1
select JSON_VALUE(val,'$.Id') as ID,
JSON_VALUE(val,'$.Name') as FirstName,
JSON_VALUE(val,'$.StudentId') as LastName,
JSON_VALUE(val,'$.CreatedDate') as Country,
JSON_VALUE(val,'$.Location') as PostCode
from
(
select value as val from openJSON(#table2Items)
) AS Table2JSON
It is working exactly as you wanted. At the end, the two select statements return the tables as you mentioned. Just add them to your desired table using insert into select. I have also tried adding another object to table1 array and verified that it is working fine i.e. returning two rows for two objects. Hope this helps
If SQL Version 2016+ use OPENJSON AND with_clause:
https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-2017
DECLARE #JsonData NVARCHAR(MAX);
SET #JsonData = N'
{
"table1": {
"Name": "table1",
"Items": [
{
"Id": 1,
"FirstName": "John",
"LastName": "Wen",
"Country": "UK",
"PostCode": 1234,
"Status": false,
"Date": "2018-09-18T08:30:32.91"
},
{
"Id": 2,
"FirstName": "John1",
"LastName": "Wen1",
"Country": "UK1",
"PostCode": 12341,
"Status": true,
"Date": "2018-09-15T08:30:32.91"
}
]
},
"table2": {
"Name": "table2",
"Items": [
{
"Id": 1,
"Name": "leo",
"StudentId": 102,
"CreatedDate": "2018-09-18",
"Location": "USA"
}
]
}
}
';
--Table1
SELECT [a].[Id]
, [a].[FistName]
, [a].[Lastname]
, [a].[Country]
, [a].[PostCode]
, [a].[Status]
, [a].[Date]
FROM
OPENJSON(#JsonData, '$.table1.Items')
WITH (
[Id] INT '$.Id'
, [FistName] NVARCHAR(200) '$.FirstName'
, [Lastname] NVARCHAR(200) '$.LastName'
, [Country] NVARCHAR(200) '$.Country'
, [PostCode] NVARCHAR(200) '$.PostCode'
, [Status] NVARCHAR(200) '$.Status'
, [Date] DATETIME '$.Date'
) [a];
--Table2
SELECT [a].[Id]
, [a].[Name]
, [a].[StudentId]
, [a].[CreatedDate]
, [a].[Location]
FROM
OPENJSON(#JsonData, '$.table2.Items')
WITH (
[Id] INT '$.Id'
, [Name] NVARCHAR(200) '$.Name'
, [StudentId] INT '$.StudentId'
, [CreatedDate] DATETIME '$.CreatedDate'
, [Location] NVARCHAR(200) '$.Location'
) [a];
Try using OPENJSON function in SQL Server:
ReferenceLink_1
ReferenceLink_2