How to get array index from Oracle JSON column? - json

Having a JSON like this (I know that JSON doesn't support comments. Used in this case to illustrate the idea):
{
"people": [
{ --// <-- index 0
"id": 100,
"name": "John Doe"
},
{ --// <-- index 1
"id": 101,
"name": "Jane Roe"
}
]
}
We can select values from specific elements in the array doing something like this:
SELECT name
FROM JSON_TABLE(
'{
"people": [
{
"id": 100,
"name": "John Doe"
},
{
"id": 101,
"name": "Jane Roe"
},
]
}', '$.people[*]'
COLUMNS(
ID NUMBER PATH '$.id',
NAME VARCHAR2 PATH '$.name'
)
) info
WHERE info.id = 101
Result:
NAME
--------
Jane Roe
Is there a way to get the element index in the array? Something like:
SELECT array_index --// <-- how get the array index of the element found?
FROM JSON_TABLE(
--// ...
) info
WHERE info.id = 101
Result:
ARRAY_INDEX
-----------
1
Is possible to do something like this using JSON support in Oracle 12c?

COLUMNS(
idx FOR ORDINALITY,
ID NUMBER PATH '$.id',
NAME VARCHAR2 PATH '$.name'
)
should work for you

As ArtBajji wrote, the rows itself dont have a index. Therefor you need to create a "fake id" that is relatively fixed.
This can be achieved by giving your data a order(order by id) and a index (rownum). Then select from this "modified" table
SELECT name, indx FROM (
SELECT ind.*, rownum "INDX"
FROM JSON_TABLE(
'{
"people": [
{
"id": 100,
"name": "John Doe"
},
{
"id": 101,
"name": "Jane Roe"
},
]
}', '$.people[*]'
COLUMNS(
ID NUMBER PATH '$.id',
NAME VARCHAR2 PATH '$.name'
)
) ind order by id) tab
WHERE tab.id = 101
Yields 1 for id 100 and 2 for id 101.
Be aware that the index of the elements 100 and 101 grow by one if you insert a item with a id < 100.

Related

How to get a specific object in an JSON array in MySQL?

I have a JSON column "jobs" that looks like this:
[
{
"id": "1",
"done": "100",
"target": "100",
"startDate": "123123132",
"lastAction": "123123132",
"status": "0"
},
{
"id": "2",
"done": "10",
"target": "20",
"startDate": "2312321",
"lastAction": "2312321",
"status": "1"
}
]
I want to filter the array by object key values. For example: To find all items that have target > done, status != 0 and lastAction is yesterday to get response like this:
[
{
"id": "1",
"done": "19",
"target": "100",
"startDate": "123123132",
"lastAction": "123123132",
"status": "0"
}
]
I know I can extract the data to a JSON_TABLE() to do the filtering but I don't get the original object back(unless I recreate it back) and the solution is not dynamic.
Can this kind of array filtering can really be done in MySQL?
SELECT JSON_PRETTY(JSON_EXTRACT(jobs.jobs, CONCAT('$[', j.rownum-1, ']'))) AS object
FROM jobs
CROSS JOIN JSON_TABLE(
jobs.jobs, '$[*]' COLUMNS(
rownum for ordinality,
done int path '$.done',
target int path '$.target',
status int path '$.status'
)
) as j
WHERE j.target > j.done AND j.status != 0;
You also mentioned a condition on lastAction, but the example values you gave are not valid dates, so I'll leave that enhancement to you. The example above demonstrates the technique.
Yes it is possible to do it using the JSON_EXTRACT and JSON_SEARCH functions.
Let's say your table is named tbl_Jobs and the jobs column is of type JSON.
SELECT * FROM tbl_Jobs
WHERE JSON_EXTRACT(jobs, "$[*].target") = JSON_EXTRACT(jobs, "$[*].done")
AND JSON_EXTRACT(jobs, "$[*].status") != 0
AND JSON_SEARCH(jobs, 'one', DATE_SUB(CURDATE(), INTERVAL 1 DAY), NULL, "$[*].lastAction") IS NOT NULL

Retrieve JSON from sql

My json format in one of the sql columns "jsoncol" in the table "jsontable" is like below.
Kindly help me to get this data using JSON_QUERY or JSON_VALUE
Please pay attention to the brackets and double quotes in the key value pairs...
{
"Company": [
{
"Info": {
"Address": "123"
},
"Name": "ABC",
"Id": 999
},
{
"Info": {
"Address": "456"
},
"Name": "XYZ",
"Id": 888
}
]
}
I am trying to retrieve all the company names using sql query. Thanks in advance
You can use:
SELECT j.name
FROM table_name t
CROSS APPLY JSON_TABLE(
t.value,
'$.Company[*]'
COLUMNS(
name VARCHAR2(200) PATH '$.Name'
)
) j
Which, for the sample data:
CREATE TABLE table_name (
value CLOB CHECK (value IS JSON)
);
INSERT INTO table_name (value)
VALUES ('{
"Company": [
{
"Info": {
"Address": "123"
},
"Name": "ABC",
"Id": 999
},
{
"Info": {
"Address": "456"
},
"Name": "XYZ",
"Id": 888
}
]
}');
Outputs:
NAME
ABC
XYZ
db<>fiddle here
You can easily use JSON_TABLE() function for this case rather provided the DB version is at least 12.1.0.2 such as
SELECT name
FROM jsontable,
JSON_TABLE(jsoncol,
'$' COLUMNS(NESTED PATH '$."Company"[*]'
COLUMNS(name VARCHAR2 PATH '$."Name"')))
Demo

JSON_QUERY to do a "Select Many"

I have a JSON variable that looks like this (the real one is more complex):
DECLARE #myJson VARCHAR(3000) = '{
"CustomerId": "123456",
"Orders": [{
"OrderId": "852",
"OrderManifests": [{
"ShippedProductId": 884,
"ProductId": 884
}, {
"ShippedProductId": 951,
"ProductId": 2564
}
]
}, {
"OrderId": "5681",
"OrderManifests": [{
"ShippedProductId": 198,
"ProductId": 4681
}, {
"ShippedProductId": 8188,
"ProductId": 8188
}, {
"ShippedProductId": 144,
"ProductId": 8487
}
]
}
]
}'
In the end, I need to know if any of the ShippedProductId values match their corresponding ProductId (in the same JSON object).
I started in by trying to get a list of all the OrderManifests. But while this will get me the array of orders:
SELECT JSON_QUERY(#myJson, '$.Orders')
I can't seem to find a way to get a list of all the OrderManifests across all the entries in the Orders array. This does not work:
SELECT JSON_QUERY(#myJson, '$.Orders.OrderManifests')
Is there a way to do a Select Many kind of query to get all the OrderManifests in the Orders array?
Use OPENJSON and CROSS APPLY to drill down into your objects.
This should do it for you:
SELECT j.CustomerId,o.OrderId, m.ShippedProductId, m.ProductId
FROM OPENJSON(#myJson)
WITH (
CustomerId NVARCHAR(1000),
Orders NVARCHAR(MAX) AS JSON
) j
CROSS APPLY OPENJSON(j.Orders)
WITH (
OrderId NVARCHAR(1000),
OrderManifests NVARCHAR(MAX) AS JSON
) o
CROSS APPLY OPENJSON(o.OrderManifests)
WITH (
ShippedProductId INT,
ProductId int
) m
WHERE m.ShippedProductId = m.ProductId;
This query returns:
CustomerId | OrderId | ShipedProductId | ProductId
------------+-----------+-------------------+-------------
123456 | 852 | 884 | 884
------------+-----------+-------------------+-------------
123456 | 5681 | 8188 | 8188

How do you run a query that returns a JSON array into a PL/SQL table?

I have the following code:
select tab.*
from (SELECT '{ "name":"John",
"age":30,
"cars": [
{"name":"Ford", "models":[ "Fiesta", "Focus", "Mustang"]},
{"name":"BMW", "models":[ "320", "X3", "X5"]},
{"name":"Fiat", "models":[ "500", "Panda"]}
]}' AS DATAx FROM dual),
JSON_TABLE(DATAx, '$'
columns ("A_json" varchar2(100) path '$.age'
,"B_check" varchar2(100) path '$.name'
,"C_check" varchar2(100) path '$.cars[*].name')) tab
which shows the following table when run:
A_json: 30
B_check: John
C_check: NULL
I would like to obtain all names from the cars array. How could I get the following output instead?:
A_json: 30
B_check: John
C_check: Ford
A_json: 30
B_check: John
C_check: BMW
A_json: 30
B_check: John
C_check: Fiat
If I try replacing * for indexes (1,2,3) it works for one row only, but I am not able to get them all.
Nested path needs to be defined to reach to 'cars--> name'. try the below given SQL -
select tab.*
from (SELECT '{ "name":"John",
"age":30,
"cars": [
{"name":"Ford", "models":[ "Fiesta", "Focus", "Mustang"]},
{"name":"BMW", "models":[ "320", "X3", "X5"]},
{"name":"Fiat", "models":[ "500", "Panda"]}
]}' AS DATAx FROM dual),
JSON_TABLE(DATAx, '$'
columns ("A_json" varchar2(100) path '$.age'
,"B_check" varchar2(100) path '$.name'
,"C_check" varchar2(100) path '$.cars.name'
, nested path '$.cars[*]' columns(
name number path '$.name'))
) tab

Find object by key/value in an array in postgresql jsonb column

I have the following table:
CREATE TABLE api_data (
id bigserial NOT NULL PRIMARY KEY,
content JSONB NOT NULL
);
Now I insert an array like this into the content column:
[{ "id": 44, "name": "address One", "petId": 1234 },
{ "id": 45, "name": "address One", "petId": 1234 },
{ "id": 46, "name": "address One", "petId": 1111 }]
What I want next is to get exactly the objects that have the "petId" set to a given value.
I figured I could do
select val
from api_data
WHERE content #> '[{"petId":1234}]'
But that returns the whole array.
Another thing I found is this query:
select val
from api_data
JOIN LATERAL jsonb_array_elements(content) obj(val) ON obj.val->>'petId' = '1234'
WHERE content #> '[{"petId":1234}]'
Which returns the object I am looking for, but three times which matches the number of elements in the array.
What I actually need is a result like this:
[{ "id": 44, "name": "address One", "petId": 1234 },
{ "id": 45, "name": "address One", "petId": 1234 }]
If you are using Postgres 12, you can use a JSON path expression:
select jsonb_path_query_array(content, '$[*] ? (#.petId == 1234)') as content
from api_data
where content #> '[{"petId":1234}]';
If you are using an older version, you need to unnest and aggregate manually:
select (select jsonb_agg(e)
from jsonb_array_elements(d.content) as t(e)
where t.e #> '{"petId":1234}') as content
from api_data d
where d.content #> '[{"petId":1234}]'