I have two columns of json that I would like to join on id into a single select.
Sample Data
| a | b |
+------------------------------------------------+-------------------------------------+
| [{id: 1, name: "Alice"},{id:2, name: "Bob"}] | [{id: 1, age: 30}, {id:2, age: 32}] |
| [{id: 5, name: "Charlie"},{id:6, name: "Dale"} | [{id: 5, age: 20}, {id:6, age: 14}] |
Desired Output
| c |
+-------------------------------------------------------------------+
| [{id: 1, name: "Alice", age: 30},{id:2, name: "Bob", age: 32}] |
| [{id: 5, name: "Charlie", age: 20},{id:6, name: "Dale", age: 14}] |
I'd like to do something like
select
id,
name,
age
from openJson(select a from someDb) sd
with (
id int '$.id',
age int '$.age'
)
inner join (
select
id,
age
from openJson(select b from someDb)
with (
id int '$.id',
age int '$.name'
)
) x
on x.id = sd.id
I don't think that current versions of SQL Server support a MERGE function. The only option is JSON_MODIFY() function, that can either update the value of an existing property, insert a new key:value pair or delete a key.
But in your case, the more appropriate approach is to parse the stored JSON as tables using OPENJSON() with explicit schema, join the tables and rebuild the required JSON output again:
SELECT
c = (
SELECT a.id, a.name, b.age
FROM OPENJSON(v.a) WITH (
id int '$.id',
name varchar(50) '$.name'
) a
FULL JOIN OPENJSON(v.b) WITH (
id int '$.id',
age int '$.age'
) b ON a.id = b.id
FOR JSON PATH
)
FROM (VALUES
('[{"id": 1, "name": "Alice"}, {"id":2, "name": "Bob"}]', '[{"id": 1, "age": 30}, {"id":2, "age": 32}]'),
('[{"id": 5, "name": "Charlie"}, {"id":6, "name": "Dale"}]', '[{"id": 5, "age": 20}, {"id":6, "age": 14}]')
) v (a, b)
Result:
c
--------------------------------------------------------------------
[{"id":1,"name":"Alice","age":30},{"id":2,"name":"Bob","age":32}]
[{"id":5,"name":"Charlie","age":20},{"id":6,"name":"Dale","age":14}]
Related
Is it possible to dynamically flatten json using snowflake function ?
select key,value from table a ,lateral flatten(input => variant_column)
gives
which need to be converted as
I've assumed your source JSON is something like this:
[
{
"empname": "e1",
"empid": 123
},
{
"empname": "e2",
"empid": 456
}
]
Based on this, you can achieve the output you want using:
select
s.value:empname::varchar as empname,
s.value:empid::number as empid
from
json j,
lateral flatten (input => j.src, path => '', mode => 'ARRAY') s
;
Full example replication code:
create or replace table json (src variant);
insert into json(src) select parse_json($$
[
{
"empname": "e1",
"empid": 123
},
{
"empname": "e2",
"empid": 456
}
]
$$
);
select * from json;
select
s.value:empname::varchar as empname,
s.value:empid::number as empid
from
json j,
lateral flatten (input => j.src, path => '', mode => 'ARRAY') s
;
I'm using SQLite3 on NodeJS and have a database in memory with a relation between table1 and table2. fk field in table2 is id in table1.
table1 :
id
value1
value2
1
v1_t1
v2_t1
table2 :
id
value1
fk
1
v1_t2
1
2
v2_t2
1
When I run this query:
SELECT * from table1 t1 INNER JOIN table2 t2 ON t2.fk=t1.id WHERE t1.id=1;
Result is :
[
{
id: 1,
value1: v1_t2,
fk:1
},
{
id: 2,
value1: v2_t2,
fk:1
}
]
But I want :
[
{
fk: 1,
value1: "v1_t1",
value2: "v2_t1",
result: [
{
id: 1,
value1: "v1_t2",
fk: 1
},
{
id: 2,
value1: "v2_t2",
fk: 1
}
]
}
]
Is this possible or should I use a non-relational database?
You can use SQLite's JSON1 Extension functions:
SELECT json_object(
'fk', t2.fk,
'value1', t1.value1,
'value2', t1.value2,
'result',
json_group_array(json_object('id', t2.id, 'value1', t2.value1, 'fk', t2.fk))
) col
FROM table1 t1 INNER JOIN table2 t2
ON t2.fk = t1.id
WHERE t1.id = 1;
See the demo.
I am trying to update a log with JSON in SQL Server 2017. I can update a data point with json_value, which covers a few cases, but would ultimately like to join in incoming JSON.
Sample table:
key | col_1 | col_2 | col_3
----+-------------------------------+---------------|-----------------
1 | json.lines[0].data.meta.data | json.lines[0] | json.header.note
2 | json.lines[1].data.meta.data} | json.lines[1] | json.header.note
3 | json.lines[2].data.meta.data} | json.lines[2] | json.header.note
I'd like to update a single property in col_1 and update col_2 with an object as as as string.
Sample JSON:
declare #json nvarchar(max) = '[{
header: {
note: 'some note'
}, lines: [{
data {
id: {
key: 0,
name: 'item_1'
},
meta: {
data: 'item_1_data'
}
}, {...}, {...}
}]
}]'
Query:
update logTable set
col_1 = json_value(#json,'$.lines[__index__].data.meta.data'), -- what would the syntax for __index__ be?
col_2 = j.lines[key], -- pseudo code
col_3 = json_value(#json, '$'.header.note')
inner join openjson(#json) j
on json_value(#json,'$.line[?].id.key') = logTable..key -- ? denotes indices that I'd like to iterate = join over
Expected Output:
key | col_1 | col_2 | col_3
----+---------------+----------------------------|---------
1 | 'item_1_data' | 'data: { id: { key: 0...}' | '{header: { note: ...} }'
2 | 'item_2_data' | 'data: { id: { key: 1...}' | '{header: { note: ...} }'
3 | 'item_3_data' | 'data: { id: { key: 2...}' | '{header: { note: ...} }'
I'm not sure how to handle iterating over the $.line indices, but think a join would solve this if properly implemented.
How can I join to arrays of objects to update SQL rows by primary key?
Original answer:
You may try to parse your JSON using OPENJSON with explicit schema (note, that your JSON is not valid):
Table and JSON:
CREATE TABLE #Data (
[key] int,
col_1 nvarchar(100),
col_2 nvarchar(max)
)
INSERT INTO #Data
([key], [col_1], [col_2])
VALUES
(1, N'', N''),
(2, N'', N''),
(3, N'', N'')
DECLARE #json nvarchar(max) = N'[{
"lines": [
{
"data": {
"id": {
"key": 1,
"name": "item_1"
},
"meta": {
"data": "item_1_data"
}
}
},
{
"data": {
"id": {
"key": 2,
"name": "item_2"
},
"meta": {
"data": "item_2_data"
}
}
},
{
"data": {
"id": {
"key": 3,
"name": "item_3"
},
"meta": {
"data": "item_3_data"
}
}
}
]
}]'
Statement:
UPDATE #Data
SET
col_1 = j.metadata,
col_2 = j.data
FROM #Data
INNER JOIN (
SELECT *
FROM OPENJSON(#json, '$[0].lines') WITH (
[key] int '$.data.id.key',
metadata nvarchar(100) '$.data.meta.data',
data nvarchar(max) '$' AS JSON
)
) j ON #Data.[key] = j.[key]
Update:
Header is common for all rows, so use JSON_QUERY() to update the table:
Table and JSON:
CREATE TABLE #Data (
[key] int,
col_1 nvarchar(100),
col_2 nvarchar(max),
col_3 nvarchar(max)
)
INSERT INTO #Data
([key], col_1, col_2, col_3)
VALUES
(1, N'', N'', N''),
(2, N'', N'', N''),
(3, N'', N'', N'')
DECLARE #json nvarchar(max) = N'[{
"header": {
"note": "some note"
},
"lines": [
{
"data": {
"id": {
"key": 1,
"name": "item_1"
},
"meta": {
"data": "item_1_data"
}
}
},
{
"data": {
"id": {
"key": 2,
"name": "item_2"
},
"meta": {
"data": "item_2_data"
}
}
},
{
"data": {
"id": {
"key": 3,
"name": "item_3"
},
"meta": {
"data": "item_3_data"
}
}
}
]
}]'
Statement:
UPDATE #Data
SET
col_1 = j.metadata,
col_2 = j.data,
col_3 = JSON_QUERY(#json, '$[0].header')
FROM #Data
INNER JOIN (
SELECT *
FROM OPENJSON(#json, '$[0].lines') WITH (
[key] int '$.data.id.key',
metadata nvarchar(100) '$.data.meta.data',
data nvarchar(max) '$' AS JSON
)
) j ON #Data.[key] = j.[key]
Given this sort of data set
records = [
{id: 1, name: "John Smith", age: 49},
{id: 2, name: "Jane Brown", age: 45},
{id: 3, name: "Tim Jones", age: 60},
{id: 4, name: "Blake Owen", age: 78}
]
How do I filter the records to return a reduced array that is just over the age of 50.
Say the return array is
over50s = // something
I've looked at lots of similar code but it's just quite coming together for me.
Thanks for your help!
How about this sample script? The result over50s is retrieved using filter().
Sample script :
records = [
{id: 1, name: "John Smith", age: 49},
{id: 2, name: "Jane Brown", age: 45},
{id: 3, name: "Tim Jones", age: 60},
{id: 4, name: "Blake Owen", age: 78}
]
over50s = records.filter (e) -> e.age >= 50
Result :
[
{ id: 3, name: 'Tim Jones', age: 60 },
{ id: 4, name: 'Blake Owen', age: 78 }
]
If I misunderstand your question, please tell me. I would like to modify.
The answer is perfect, but I wanted to add the "Coffeescript way" using comprehensions:
over50s = (record for record in records when record.age > 50)
Explanation :
for record in records
console.log(record)
# This would loop over the records array,
# and each item in the array will be accessible
# inside the loop as `record`
console.log(record) for record in records
# This is the single line version of the above
console.log(record) for record in records when record.age > 50
# now we would only log those records which are over 50
over50s = (record for record in records when record.age > 50)
# Instead of logging the item, we can return it, and as coffeescript
# implicitly returns from all blocks, including comprehensions, the
# output would be the filtered array
# It's shorthand for:
over50s = for record in records
continue unless record.age > 50
record
# This long hand is better for complex filtering conditions. You can
# just keep on adding `continue if ...` lines for every condition
# Impressively, instead of ending up with `null` or `undefined` values
# in the filtered array, those values which are skipped by `continue`
# are completely removed from the filtered array, so it will be shorter.
I need to output json out from the query.
Input data:
Documents:
==========
id | name | team
------------------
1 | doc1 | {"authors": [1, 2, 3], "editors": [3, 4, 5]}
Persons:
========
id | name |
--------------
1 | Person1 |
2 | Person2 |
3 | Person3 |
4 | Person4 |
5 | Person5 |
Query:
select d.id, d.name,
(select jsonb_build_object(composed)
from
(
select teamGrp.key,
(
select json_build_array(persAgg) from
(
select
(
select jsonb_agg(pers) from
(
select person.id, person.name
from
persons
where (persList.value)::int=person.id
) pers
)
from
json_array_elements_text(teamGrp.value::json) persList
) persAgg
)
from
jsonb_each_text(d.team) teamGrp
) teamed
) as teams
from
documents d;
and i expect the following output:
{"id": 1, "name": "doc1", "teams":
{"authors": [{"id": 1, "name": "Person1"}, {"id": 2, "name": "Person2"}, {"id": 3, "name": "Person3"}],
"editors": [{"id": 3, "name": "Person3"}, {"id": 5, "name": "Person5"}, {"id": 5, "name": "Person5"}]}
But received an error:
ERROR: more than one row returned by a subquery used as an expression
Where is the problem and how to fix it?
PostgreSQL 9.5
I think the following (super complicated query) should to it:
SELECT
json_build_object(
'id',id,
'name',name,
'teams',(
SELECT json_object_agg(team_name,
(SELECT
json_agg(json_build_object('id',value,'name',Persons.name))
FROM json_array_elements(team_members)
INNER JOIN Persons ON (value#>>'{}')::integer=Persons.id
)
)
FROM json_each(team) t(team_name,team_members)
)
)
FROM Documents;
I am using subqueries where I run json aggregates.