I'm making a foray into JSON, I'd like to add a user to multiple groups: insert an JSON array into a table.
Ideally, the JSON would look like this:
'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Groups": [{"0", "1", "5"}]
"Better_Groups": [{"ID":"0", "ID":"1", "ID":"5"}]
}'
Currently, I can do it with JSON like this:
'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Groups": "1,2,3,4"
}'
then "unpack" it with the following ditty:
declare #groups varchar(1000)
select #groups = Groups from openjson(#json)
WITH
(
Groups nvarchar(100) '$.Groups'
)
print #groups
select value from string_split(#groups, ',')
which returns a nice little table like so:
Value
1
2
3
4
Problem This is bad JSON and the Web developer will make fun of me.
Question How do you propely unpack a JSON array in SQL Server?
update:
The final JSON used looks like so:
#json =
'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Groups": "1,2,3,4",
"Better_Groups": ["0", "1", "5"]
}'
Assuming that "groups" element is an array:
DECLARE #json NVARCHAR(MAX) =
N'{
"Email": "WMogh#starfleet.gov",
"Prefix":null,
"FirstName": "Worf",
"MiddleInitial": "",
"LastName": "Mogh",
"Suffix": "Son Of",
"Title" :"Commander",
"Better_Groups": ["0", "1", "5"]
}';
SELECT s.value
FROM OPENJSON(JSON_QUERY(#json, '$.Better_Groups')) s;
db<>fiddle demo
Related
With the below structure of Couchbase bucket, how do I query if the nested doc has dynamic field name?
Here, I would like to return the customer docs who have account in Hyderabad
I tried to query this way but couldn't succeed.
select * from bucket where accounts.$.city = 'Hyderabad'
I was expecting to return the customer doc with email kp711#yahoo.com but couldn't succeed.
Couchbase docs
[
{
"type": "customer",
"customer_id": <UUID4>,
"user_type": "owner",
"first_name": "",
"last_name": "",
"email": "kp711#yahoo.com",
"password": "",
"phone_number": 11111,
"accounts": {
<account_id which is UUID4>: {
"amount": "500",
"city": "Hyderabad"
}
}
},
{
"type": "customer",
"customer_id": <UUID4>,
"user_type": "employee",
"first_name": "",
"last_name": "",
"email": "px800#yahoo.com",
"password": "",
"phone_number": 33333,
"accounts": {
<account_id which is UUID4>: {
"amount": "500",
"city": "Chennai"
}
}
}
]
Is there a way in Couchbase to fetch in this way?
SELECT b.*
FROM bucket AS b
WHERE ANY v IN OBJECT_VALUES(b.accounts) SATISFIES v.city = 'Hyderabad' END;
OR
SELECT b.*
FROM bucket AS b
WHERE ANY n:v IN b.accounts SATISFIES v.city = 'Hyderabad' END;
The answer from VSR provides a neat solution to the problem. Remember also that you will want to provide an index in order to leverage the Couchbase query services optimally. I'm guessing (based on your doc examples) that you will have multiple document types included in your bucket. With that in mind, here is an example index create statement:
create index idxTypeCustomer on bucket(type) where type = 'customer';
Now you can use it as part of your WHERE clause:
SELECT b.*
FROM bucket AS b
WHERE type = 'customer'
AND ANY v IN OBJECT_VALUES(b.accounts) SATISFIES v.city = 'Hyderabad' END;
How to retrieve values from employment_types (type, salary) and skills (name, level) arrays and show them in columns? I tried with employment_types and it doesn't work not to mention skills:
declare #json nvarchar(max)
set #json = '[
{
"title": "IT Admin",
"experience_level": "mid",
"employment_types": [
{
"type": "permanent",
"salary": null
}
],
"skills": [
{
"name": "Security",
"level": 3
},
{
"name": "WIFI",
"level": 3
},
{
"name": "switching",
"level": 3
}
]
},
{
"title": "Lead QA Engineer",
"experience_level": "mid",
"employment_types": [
{
"type": "permanent",
"salary": {
"from": 7000,
"to": 13000,
"currency": "pln"
}
}
],
"skills": [
{
"name": "Embedded C",
"level": 4
},
{
"name": "Quality Assurance",
"level": 4
},
{
"name": "C++",
"level": 4
}
]
}
]';
SELECT *
FROM OPENJSON(#JSON, '$.employment_types')
WITH
(
type nvarchar(50) '$.type',
salary varchar(max) '$.salary'
)
There are almost 7000 records and I'd like to show mentioned above columns from all of them.
It's hard to know exactly what you want, given that both employment_types and skills are arrays. But assuming employment_types always has only one element, you could do something like this
SELECT
j1.title,
j1.experience_level,
j1.employment_type,
salary = j1.salary_currency + ' ' + CONCAT(j1.salary_from, ' - ', j1.salary_to),
j2.name,
j2.level
FROM OPENJSON(#JSON)
WITH (
title nvarchar(100),
experience_level nvarchar(10),
employment_type nvarchar(50) '$.employment_types[0].type',
salary_from int '$.employment_types[0].salary.from',
salary_to int '$.employment_types[0].salary.to',
salary_currency char(3) '$.employment_types[0].salary.currency',
skills nvarchar(max) AS JSON
) j1
CROSS APPLY OPENJSON(j1.skills)
WITH
(
name nvarchar(50),
level int
) j2
db<>fiddle
Since we are pulling data directly from the root object, we don't need a JSON path argument. OPENJSON will automatically break out an array into separate rows. If you just wanted employment_types, you could go directly to that with a path argument.
employment_types[0] means to only get the first element of the array. If you want all the elements, you will need another OPENJSON
Note the use of AS JSON for skills, this means that the entire JSON array is pulled out, and can then be pushed through another call to OPENJSON
I want to access over an array object in JSON by index with a variable. Consider the following code:
declare #pjson nvarchar(max)='{
"store":{
"storeId": 100,
"name": "TEST",
"lastUpdatedBy": "MULE",
"location": {
declare #pjson nvarchar(max)='{
"store":{
"storeId": 100,
"name": "TEST",
"lastUpdatedBy": "MULE",
"location": {
"addresses": [
{
"addressType": "MAIN",
"name": "Name1",
"name2": "Name2",
"address": "Address1",
"address2": "Address2",
"city": "City",
"lastUpdateBy": "MULE"
},
{
"addressType": "SECONDARY",
"name": "Name1",
"name2": "Name2",
"address": "Address1",
"address2": "Address2",
"city": "City",
"lastUpdateBy": "MULE"
},
{
"addressType": "BILLING",
"name": "Name1",
"name2": "Name2",
"address": "Address1",
"address2": "Address2",
"city": "City",
"lastUpdateBy": "MULE"
}
]
}
}
}'
Declare #counter1 INT = 0;
Print JSON_VALUE(#pjson,N'lax $.store.location.addresses[#counter1].addressType')
I get an error:
JSON path is not properly formatted. Unexpected character '#' is found
at position 31.
If I try directly by passing number as
Declare #counter1 INT = 0;
Print JSON_VALUE(#pjson,N'lax $.store.location.addresses[0].addressType')
I get the expected result
MAIN
Is there something that I am missing while passing the variable?
I don't think that you can use a T-SQL variable directly as part of the path parameter in the JSON_VALUE() call, but you may try one of the following approaches:
Concatenate the #counter variable in the path parameter (SQL Server 2017 is needed).
Parse the JSON with OPENJSON() and the appropriate WHERE clause.
JSON:
DECLARE #counter INT = 0;
DECLARE #pjson nvarchar(max) = N'{
"store":{
"storeId":100,
"name":"TEST",
"lastUpdatedBy":"MULE",
"location":{
"addresses":[
{
"addressType":"MAIN",
"name":"Name1",
"name2":"Name2",
"address":"Address1",
"address2":"Address2",
"city":"City",
"lastUpdateBy":"MULE"
},
{
"addressType":"SECONDARY",
"name":"Name1",
"name2":"Name2",
"address":"Address1",
"address2":"Address2",
"city":"City",
"lastUpdateBy":"MULE"
},
{
"addressType":"BILLING",
"name":"Name1",
"name2":"Name2",
"address":"Address1",
"address2":"Address2",
"city":"City",
"lastUpdateBy":"MULE"
}
]
}
}
}'
Statement with variable concatenation:
SELECT JSON_VALUE(
#pjson,
CONCAT(N'lax $.store.location.addresses[', #counter, N'].addressType')
)
Statement with OPENJSON():
SELECT JSON_VALUE([value], '$.addressType')
FROM OPENJSON(#pjson, 'lax $.store.location.addresses')
WHERE CONVERT(int, [key]) = #counter
Result:
(No column name)
----------------
MAIN
Try following:
DECLARE #pJson NVARCHAR(4000)
-- ...
DECLARE #Counter INT = 0
DECLARE #PathString NVARCHAR(1000)
SET #PathString = N'lax $.store.location.addresses[' + CAST(#Counter AS NVARCHAR(50)) + N'].addressType'
PRINT JSON_VALUE(#Pjson,#PathString)
How to merge new array elements into an existing Json property?
Our two json objects look like this and we want to append the items of the 2nd json object to the 1st json object. (See desired result)
Is there a way to achieve this with JSON_MODIFY?
The microsoft documentation doesn't really show any example of multiple elements being merged into the already existing array. Only a single element. But we have a list of multiple elements that need to be merged.
Edit:
JSON_MODIFY(#json1, 'append $.Items', JSON_QUERY(#json2)) seems to create new array brackets instead of merging the items into the array.
Json Object 1:
DECLARE #json1 NVARCHAR(MAX) = '{
"id": 1,
"Items": [
{
"id": 1,
"name" : "Item #1"
},
{
"id": 2,
"name" : "Item #2"
}
]
}'
Json Object 2:
DECLARE #json2 NVARCHAR(MAX)='{
"Items": [
{
"id": 3,
"name": "Item #3"
},
{
"id": 4,
"name": "Item #4"
}
]
}'
Desired Result:
{
"id": 1,
"Items": [
{
"id": 1,
"name": "Item #1"
},
{
"id": 2,
"name": "Item #2"
},
{
"id": 3,
"name": "Item #3"
},
{
"id": 4,
"name": "Item #4"
}
]
}
It's Cumbersome but possible to achieve with SQL Server's built it JSON support.
First, set proper sample data (Please save us this step in your future questions):
DECLARE #Json1 nvarchar(max) =
'{
"id": 1,
"Items": [
{
"id": 1,
"name" : "Item #1"
},
{
"id": 2,
"name" : "Item #2"
}
]
}',
#Json2 nvarchar(max) =
'{
"Items": [
{
"id": 3,
"name": "Item #3"
},
{
"id": 4,
"name": "Item #4"
}
]
}';
Then, wrap a union all query containing openjson and json_query for each one of the variables with a common table expression:
With cteArray as
(
SELECT *
FROM OPENJSON(JSON_QUERY(#Json1, '$.Items'))
WITH(
Id int '$.id',
Name varchar(100) '$.name'
)
UNION ALL
SELECT *
FROM OPENJSON(JSON_QUERY(#Json2, '$.Items'))
WITH(
Id int '$.id',
Name varchar(100) '$.name'
)
)
The result of that union all query is this:
Id Name
1 Item #1
2 Item #2
3 Item #3
4 Item #4
Then, select the id from the first json using json_value, and add a subquery to select everything from the cte with for json path. Add another for json path and specify without_array_wrapper to the outer query:
SELECT JSON_VALUE(#Json1, '$.id') As id,
(
SELECT *
FROM cteArray
FOR JSON PATH
) as Items
FOR JSON PATH,
WITHOUT_ARRAY_WRAPPER
The final result:
{
"id": "1",
"Items": [{
"Id": 1,
"Name": "Item #1"
}, {
"Id": 2,
"Name": "Item #2"
}, {
"Id": 3,
"Name": "Item #3"
}, {
"Id": 4,
"Name": "Item #4"
}
]
}
You can see a live demo on Db<>Fiddle
Your problem can be solved by
SELECT dbo.udf_native_json_merge(#json1,#json2,null)
We faced similar issues trying to merge JSONs in MS SQL. We also wanted it to be recursive and allow us to define a strategy for arrays like "union", "concat" and "replace".
Our solution for JSON manipulations like merge, JSON path expressions and more is open source and available # Github
Feel free to use, comment and contribute so we can further improve JSON methods for MS SQL.
Not pretty but this will merge text and object elements. I'm sure it isn't bullet proof. Offered only as a potential solution.
Declare #json1 nvarchar(max) = '{"id":1,"messages":[{"type":"Info","text":"message1"},{"type":"Info","text":"message2"}]}'
Declare #json2 nvarchar(max) = '{"id":1,"messages":["justPlanText",{"type":"Info","text":"message3"},{"type":"Info","text":"message4"}]}'
select #json1 = case
when isjson(m.value) = 1 then
json_modify(#json1,'append $.messages',json_query(m.value))
else
json_modify(#json1,'append $.messages',m.value)
end
from openjson(json_query(#json2,'$.messages')) m
select #json1
Results for #json1:
{"id":1,"messages":[{"type":"Info","text":"message1"},{"type":"Info","text":"message2"},"justPlanText",{"type":"Info","text":"message3"},{"type":"Info","text":"message4"}]}
I have a variable which is constructed as follows extracting data using Spark SQL:
{
"resourceType" : "Test1",
"count" : 10,
"entry": [{
"id": "112",
"gender": "female",
"birthDate": 1213999
}, {
"id": "urn:uuid:002e27cf-3cae-4393-89c5-1b78050d9428",
"resourceType": "Encounter"
}]
}
I want the output in the following format:
{
"resourceType" : "Test1",
"count" : 10,
"entry" :[
"resource" :{
"id": "112",
"gender": "female",
"birthDate": 1213999
},
"resource" :{
"id": "urn:uuid:002e27cf-3cae-4393-89c5-1b78050d9428",
"resourceType": "Encounter"
}]
}
I am basically new to Scala :), would need help in this.
EDIT: Adding the scala code to create the JSON:
val bundle = endresult.groupBy("id").agg(count("*") as "total",collect_list("resource") as "entry").
withColumn("resourceType", lit("Bundle")).
drop("id").
select(to_json(struct("resourceType","entry"))).
map(row => row.getString(0).
replace("\"entry\":[\"{", "\"entry\":[{").
replace("}\"]}","}]}"). // Should be at the end of the string ONLY (we might switch to regex instead
replace("}\",\"{","},{")
replace("\\\"", "\"")
)