query json array with Oracle 12cR2 json features - json

I am using Oracle (12cR2) json but couldn't find a way to query arrays containing certain elements. Here is the test code:
CREATE TABLE json_array (
id NUMBER NOT NULL,
array CLOB,
CONSTRAINT json_array_pk PRIMARY KEY (id),
CONSTRAINT json_array_chk_1 CHECK (array IS JSON)
);
INSERT INTO json_array (id, array) VALUES (1, '{"a":[1, 3]}');
INSERT INTO json_array (id, array) VALUES (2, '{"a":[2, 4, 6]}');
INSERT INTO json_array (id, array) VALUES (3, '{"a":[1, 2, 5]}');
INSERT INTO json_array (id, array) VALUES (4, '{"a":[2, 5]}');
INSERT INTO json_array (id, array) VALUES (5, '{"a":[5]}');
INSERT INTO json_array (id, array) VALUES (6, '{"a":[5, 2]}');
COMMIT;
Create a domain index:
CREATE SEARCH INDEX idx_json_array ON json_array (array) FOR JSON;
I want to find all rows containing the array element 2 and 5, regardless their order in the array, i.e. the SQL should return the rows with id 3, 4, 6.
I tried many options:
SQL1:
select * from json_array j -- return any arrays containing 2
where json_exists(j.array, '$?(#.a[0] == 2)');
==> return the rows containing 2: id = 2, 3, 4, 6
SQL2:
select * from json_array j -- return arrays containing 2 at index 1
where json_exists(j.array, '$?(#.a[0] == 2 || #.a[0] == 5)');
==> return rows containing 2 or 5: id = 2, 4, 5, 6
SQL3:
select * from json_array j -- return arrays containing 2 at index 1
where json_exists(j.array, '$?(#.a[0] == 2 && #.a[0] == 5)');
==> return no row
SQL4:
select * from json_array j -- returns arrays containing 2 OR 5
where json_textcontains(j.array, '$.a', '[2,5]');
==> return rows containing 2 or 5: id = 2, 3, 4, 5, 6
SQL5:
select * from json_array j
where json_textcontains(j.array, '$.a', '{[2] & [5]}');
==> returns rows containing 2 AND 5, with 2 preceding 5
The only SQL that returns what I want is:
SQL6:
select * from json_array j
where json_textcontains(j.array, '$.a', '[2]') AND json_textcontains(j.array, '$.a', '[5]');
==> returns id = 3, 4, 6
But this solution can be very cumbersome when the number of elements increases.
Question: are there better option that SQL6 to return the same results?
Oracle version for testing 12c R2
Thanks in advance
James

Does this help...
SQL> with MY_TABLE as
2 (
3 select 1 as ID, '{"a":[1, 3]}' as ARRAY
4 from DUAL
5 union all
6 select 2 as ID, '{"a":[2, 4, 6]}' as ARRAY
7 from DUAL
8 union all
9 select 3 as ID, '{"a":[1, 2, 5]}' as ARRAY
10 from DUAL
11 union all
12 select 4 as ID, '{"a":[2, 5]}' as ARRAY
13 from DUAL
14 union all
15 select 5 as ID, '{"a":[5]}' as ARRAY
16 from DUAL
17 union all
18 select 6 as ID, '{"a":[5, 2]}' as ARRAY
19 from DUAL
20 )
21 select ID
22 from MY_TABLE
23 where json_exists(ARRAY,'$?(#.a == 2 && #.a == 5)')
24 /
ID
----------
3
4
6
SQL>

Related

How to split column values by comma and return it as an array

As you can see below I have Name column. I want to split it by / and return the value in array.
MyTable
Id
Name
1
John/Warner/Jacob
2
Kol
If I write a query as
Select Id, Name from MyTable
it will return
{
"id": 1,
"name": "John/Warner/Jacob",
},
{
"id": 2,
"name": "Kol",
},
Which query should I write to get below result ?
{
"id": 1,
"name": ["John", "Warner", "Jacob"],
},
{
"id": 2,
"name": ["Kol"] ,
},
Don't think you can return an array in the query itself, but you could do this...
SELECT id,
SUBSTRING_INDEX(name, '/', 1)
AS name_part_1,
SUBSTRING_INDEX(name, '/', -1)
AS name_part_2
FROM tableName;
Only way to build it as an array would be when processing the result accordingly in whatever language you are using.
You can define a function split, which is based on the fact that substring_index(substring_index(name,'/',x),'/',-1) will return the x-th part of a name when separated by '/'.
CREATE FUNCTION `test`.`SPLIT`(s varchar(200), c char, i integer) RETURNS varchar(200) CHARSET utf8mb4
DETERMINISTIC
BEGIN
DECLARE retval varchar(200);
WITH RECURSIVE split as (
select 1 as x,substring_index(substring_index(s,c,1),c,-1) as y, s
union all
select x+1,substring_index(substring_index(s,c,x+1),c,-1),s from split where x<= (LENGTH(s) - LENGTH(REPLACE(s,c,'')))
)
SELECT y INTO retval FROM split WHERE x=i ;
return retval;
END
and then do:
with mytable as (
select 1 as Id, 'John/Warner/Jacob' as Name
union all
select 2, 'Kol')
select
id, split(Name,'/',x) as name
from mytable
cross join (select 1 as x union all select 2 union all select 3) x
order by id, name;
output:
Id
name
1
Jacob
1
John
1
Warner
2
[NULL]
2
[NULL]
2
Kol
It is, of course, possible to refine this, and leave out the NULL values ...
I will not convert this output to JSON for you ...

Postgres: transform json object into array of key-value pairs

With this select:
SELECT '{"key1": "value1", "key2": "value2"}'::jsonb
can we transform the result object into the following array of objects keeping their keys and values respectively?
[{"key1": "value1"}, {"key2": "value2"}]
In my case, I have many-2-many relationship of tags and operations, and the following query with aggregation of tags into json object for each operation:
SELECT ot.operation_id, json_object_agg(t.name, t.value) AS tagsjson
FROM tag t
INNER JOIN operation_tag ot ON t.tag_id = ot.tag_id
GROUP BY ot.operation_id
This works fine except format of the result: I need array of tags [{name : value},...] instead of whole object containing tags as it's properties:
If I understand you correctly you are in a situation like the following:
your source table contains id, keys and values like the below
create table test_k_v (id int, k varchar, v varchar);
insert into test_k_v values (1, 'key1', 'value1');
insert into test_k_v values (1, 'key2', 'value2');
insert into test_k_v values (2, 'key3', 'value3');
insert into test_k_v values (2, 'key4', 'value4');
insert into test_k_v values (2, 'key5', 'value5');
which results in
defaultdb=> select * from test_k_v;
id | k | v
----+------+--------
1 | key1 | value1
1 | key2 | value2
2 | key3 | value3
2 | key4 | value4
2 | key5 | value5
(5 rows)
If so, you can create an array with all the tags with the following query:
select id, array_agg(json_build_object(k, v)) agg_res
from test_k_v
group by id;
result
id | agg_res
----+---------------------------------------------------------------------------------
2 | {"{\"key3\" : \"value3\"}","{\"key4\" : \"value4\"}","{\"key5\" : \"value5\"}"}
1 | {"{\"key1\" : \"value1\"}","{\"key2\" : \"value2\"}"}
(2 rows)

MYSQL-how to insert geometry data

I'm trying to insert geometry data using MYSQL, here is a code-example:
CREATE TABLE CARTESIAN
(
ROW_ID INT NOT NULL,
G GEOMETRY,
PRIMARY KEY(ROW_ID)
)
INSERT INTO CARTESIAN
VALUES (0,'POINT(1 1)'),
(1,'LINESTRING(2 1, 6 6)'),
(2,'POLYGON((0 5, 2 5, 2 7, 0 7, 0 5))')
When I run the INSERT I receive the message "Cannot get geometry object from data you send to the GEOMETRY field".
Can you explain me where I'm wrong?
You need to convert the text representations into GEOMETRY before you can insert them using the ST_GeomFromText function. Try this:
CREATE TABLE CARTESIAN
(
ROW_ID INT NOT NULL,
G GEOMETRY,
PRIMARY KEY(ROW_ID)
);
INSERT INTO CARTESIAN
VALUES (0,ST_GeomFromText('POINT(1 1)')),
(1,ST_GeomFromText('LINESTRING(2 1, 6 6)')),
(2,ST_GeomFromText('POLYGON((0 5, 2 5, 2 7, 0 7, 0 5))'));
SELECT * FROM CARTESIAN
Output:
ROW_ID G
0 [GEOMETRY - 25 B]
1 [GEOMETRY - 45 B]
2 [GEOMETRY - 97 B]

How to move column data to next row until end starting on specified key

I have a table named network_table with this data example:
NetworkTableID, MainID, UserID
1, 1, 1
2, 1, 2
3, 1, 3
4, 1, 4
5, 2, 5
So I want to move all the columns data of UserID to the next row starting from NetworkTableID: 2 until the end of the table and there will be another inserted row because the rows moved. I'm expecting a result like this:
NetworkTableID, MainID, UserID
1, 1, 1
2, 1, 2
3, 1, 2
4, 1, 3
5, 2, 4
6, 2, 5
This query don't work for me. It gives me a "#1093 - You can't specify target table 'network_table' for update in FROM clause" error.
UPDATE network_table
SET UserID = (SELECT UserID FROM network_table as nt2
WHERE nt2.NetworkTableID=network_table.NetworkTableID-1)
WHERE network_table.NetworkTableID > 2
Note: The UserID column is a UNIQUE key.
To maintain the last field if required save a copy:
INSERT INTO network_table (MainID,UserID)
SELECT MainID,UserID FROM network_table ORDER BY NetworkTableID DESC LIMIT 1;
and then update the table:
UPDATE network_table
left join network_table as nt2 on nt2.NetworkTableID=network_table.NetworkTableID-1
SET network_table.UserID = nt2.UserID
WHERE network_table.NetworkTableID > 2

How to search JSON array in MySQL?

Let's say I have a JSON column named data in some MySQL table, and this column is a single array. So, for example, data may contain:
[1,2,3,4,5]
Now I want to select all rows which have a data column where one of its array elements is greater than 2. Is this possible?
I tried the following, but seems it is always true regardless of the values in the array:
SELECT * from my_table
WHERE JSON_EXTRACT(data, '$[*]') > 2;
You may search an array of integers as follows:
JSON_CONTAINS('[1,2,3,4,5]','7','$') Returns: 0
JSON_CONTAINS('[1,2,3,4,5]','1','$') Returns: 1
You may search an array of strings as follows:
JSON_CONTAINS('["a","2","c","4","x"]','"x"','$') Returns: 1
JSON_CONTAINS('["1","2","3","4","5"]','"7"','$') Returns: 0
Note: JSON_CONTAINS returns either 1 or 0
In your case you may search using a query like so:
SELECT * from my_table
WHERE JSON_CONTAINS(data, '2', '$');
SELECT JSON_SEARCH('["1","2","3","4","5"]', 'one', "2") is not null
is true
SELECT JSON_SEARCH('["1","2","3","4","5"]', 'one', "6") is not null
is false
Since MySQL 8 there is a new function called JSON_TABLE.
CREATE TABLE my_table (id INT, data JSON);
INSERT INTO my_table VALUES
(1, "[1,2,3,4,5]"),
(2, "[0,1,2]"),
(3, "[3,4,-10]"),
(4, "[-1,-2,0]");
SELECT DISTINCT my_table.*
FROM my_table, JSON_TABLE(data, "$[*]" COLUMNS(nr INT PATH '$')) as ids
WHERE ids.nr > 2;
+------+-----------------+
| id | data |
+------+-----------------+
| 1 | [1, 2, 3, 4, 5] |
| 3 | [3, 4, -10] |
+------+-----------------+
2 rows in set (0.00 sec)
I use a combination of JSON_EXTRACT and JSON_CONTAINS (MariaDB):
SELECT * FROM table WHERE JSON_CONTAINS(JSON_EXTRACT(json_field, '$[*].id'), 11, '$');
I don't know if we found the solution.
I found with MariaDB a way, to search path in a array. For example, in array [{"id":1}, {"id":2}], I want find path with id equal to 2.
SELECT JSON_SEARCH('name_field', 'one', 2, null, '$[*].id')
FROM name_table
The result is:
"$[1].id"
The asterisk indicate searching the entire array
This example works for me with mysql 5.7 above
SET #j = '{"a": [ "8428341ffffffff", "8428343ffffffff", "8428345ffffffff", "8428347ffffffff","8428349ffffffff", "842834bffffffff", "842834dffffffff"], "b": 2, "c": {"d": 4}}';
select JSON_CONTAINS(JSON_EXTRACT(#j , '$.a'),'"8428341ffffffff"','$') => returns 1
notice about " around search keyword, '"8428341ffffffff"'
A possible way is to deal with the problem as string matching. Convert the JSON to string and match.
Or you can use JSON_CONTAINS.
You can use JSON extract to search and select data
SELECT data, data->"$.id" as selectdata
FROM table
WHERE JSON_EXTRACT(data, "$.id") = '123'
#ORDER BY c->"$.name";
limit 10 ;
SET #doc = '[{"SongLabels": [{"SongLabelId": "111", "SongLabelName": "Funk"}, {"SongLabelId": "222", "SongLabelName": "RnB"}], "SongLabelCategoryId": "test11", "SongLabelCategoryName": "曲风"}]';
SELECT *, JSON_SEARCH(#doc, 'one', '%un%', null, '$[*].SongLabels[*].SongLabelName')FROM t_music_song_label_relation;
result: "$[0].SongLabels[0].SongLabelName"
SELECT song_label_content->'$[*].SongLabels[*].SongLabelName' FROM t_music_song_label_relation;
result: ["Funk", "RnB"]
I have similar problem, search via function
create function searchGT(threshold int, d JSON)
returns int
begin
set #i = 0;
while #i < json_length(d) do
if json_extract(d, CONCAT('$[', #i, ']')) > threshold then
return json_extract(d, CONCAT('$[', #i, ']'));
end if;
set #i = #i + 1;
end while;
return null;
end;
select searchGT(3, CAST('[1,10,20]' AS JSON));
This seems to be possible with to JSON_TABLE function. It's available in mysql version 8.0 or mariadb version 10.6.
With this test setup
CREATE TEMPORARY TABLE mytable
WITH data(a,json) AS (VALUES ('a','[1]'),
('b','[1,2]'),
('c','[1,2,3]'),
('d','[1,2,3,4]'))
SELECT * from data;
we get the following table
+---+-----------+
| a | json |
+---+-----------+
| a | [1] |
| b | [1,2] |
| c | [1,2,3] |
| d | [1,2,3,4] |
+---+-----------+
It's possible to select every row from mytable wich has a value greater than 2 in the json array with this query.
SELECT * FROM mytable
WHERE TRUE IN (SELECT val > 2
FROM JSON_TABLE(json,'$[*]'
columns (val INT(1) path '$')
) as json
)
Returns:
+---+-----------+
| a | json |
+---+-----------+
| c | [1,2,3] |
| d | [1,2,3,4] |
+---+-----------+