How can I iterate a JSON in SQL? - mysql

So I have a JSON variable with several values like this:
"["1", "2", "3", "4"]"
What i need to do is pass that value to an SQL procedure to mount a query in which the WHERE clause adds all the values in the JSON, so something like parsing the JSON, interate it and concat it in order to get an #where similar to:
AND id=1 AND id=2 AND id=3 AND id=4
I tried something like this, as something really similar is taking place in an already existing procedure, but doesn't work:
SET #idWhere="";
IF id IS NOT NULL AND JSON_EXTRACT(id, '$[0]') IS NOT NULL THEN
SET #idWhere = CONCAT(#idWhere," AND JSON_SEARCH('",id,"','one',id) IS NOT NULL ");
END IF;
Where id is both the name of the JSON and the column name.
Thanks in advance!

If you are running MySQL 8.0, you can use json_table() to turn the array to a recordset, and then aggregate the results with group_concat():
select group_concat(concat('id = ', i) separator ' and ') id_where
from json_table(
'["1", "2", "3", "4"]',
"$[*]" columns(i int path '$')
) as t
In this demo on DB Fiddle, this yields:
| id_where |
| --------------------------------------- |
| id = 1 and id = 2 and id = 3 and id = 4 |
NB: you probably want ors, not ands.

Related

How do i batch multiple select query calls into a single mysql query or procedure

I have this function which checks friendship status between 2 users using their userIds and it is working fine.
function checkFriendShipBetweenUsers(user1Id, user2Id) {
var checkFriendShipBetweenUsersQuery = "SELECT status FROM friends WHERE (user1Id=? AND user2Id =?) OR (user1Id=? AND user2Id =?)"
var queryParameterList = [user1Id, user2Id, user2Id, user1Id]
}
I have a case in which i need to check friendship status between a user and other 3 users.
I can call above function 3 times, one for each other user to get desired result but i would like to make it with a single db call using a single query or using a mysql procedure.
function checkFriendShipBetweenUsers(user1Id, userIdList) {
var checkFriendShipBetweenUsersQuery = ""
var queryParameterList = []
}
So this query/procedure call should return 3 integers indicating user1's friendship status with users in userIdList.
Here is an example db fiddle:
db-fiddle.com/f/p5RP61V3AcawRgJcogeXey/1
given user1Id : 'a8t57h6p8n2efden' and
userIdList : ['typ3vg6xb1vt7nw2', 'cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl', '0bw87kprb97pes1crom8ceodi07r2kd0']
How do i write such query or procedure?
DEMO
-- source data
CREATE TABLE test (
id INT,
user1Id VARCHAR(100),
user2Id VARCHAR(100),
status INT
);
INSERT INTO test (id,user1Id,user2Id,status) VALUES
(1,'a8t57h6p8n2efden','typ3vg6xb1vt7nw2',0),
(2,'cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl','a8t57h6p8n2efden',1),
(3,'0bw87kprb97pes1crom8ceodi07r2kd0','a8t57h6p8n2efden',2),
(4,'a8t57h6p8n2efden','ap21wzbew0bprt5t',0);
SELECT * FROM test;
id
user1Id
user2Id
status
1
a8t57h6p8n2efden
typ3vg6xb1vt7nw2
0
2
cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl
a8t57h6p8n2efden
1
3
0bw87kprb97pes1crom8ceodi07r2kd0
a8t57h6p8n2efden
2
4
a8t57h6p8n2efden
ap21wzbew0bprt5t
0
-- searching parameters
SET #user1Id := 'a8t57h6p8n2efden';
SET #userIdList := '[
"typ3vg6xb1vt7nw2",
"cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl",
"0bw87kprb97pes1crom8ceodi07r2kd0",
"absent value"
]';
SELECT jsontable.userid, test.status
FROM JSON_TABLE( #userIdList,
'$[*]' COLUMNS ( rowid FOR ORDINALITY,
userid VARCHAR(255) PATH '$'
)) jsontable
LEFT JOIN test
ON (#user1Id, jsontable.userid) IN ( (test.user1Id, test.user2Id),
(test.user2Id, test.user1Id)
)
userid
status
typ3vg6xb1vt7nw2
0
cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl
1
0bw87kprb97pes1crom8ceodi07r2kd0
2
absent value
null
fiddle
If you do not need status value for the IDs which are not found then use INNER JOIN.
If you want to receive the output as one solid value then add according GROUP BY and aggregation. Use jsontable.rowid for to provide needed values ordering.
PS. If you won't use an aggregation then you may do not obtain rowid value - simply remove rowid FOR ORDINALITY, in this case.

Convert select result that has JSON field to JSON and use that data with JSON_VALUE()

I have a table that has some columns.
One of these columns stores data in JSON format.
I select a row from this table with FOR JSON AUTO.
My problem is that SQL Server puts quotations around the value of JSON properties but I don't want this; because I want to use the values of inner JSON with JSON_VALUE(). What can I do?
Code:
SELECT TOP 1 *
FROM users;
Result:
name lastName age favorites
John Duo 20 {"city": "paris", "color": "blue", "sport": "football"}
Code:
SELECT TOP 1 *
FROM users
FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER;
Result:
{"name":"John","lastName":"Duo","age":20,"favorites":"{\"city\": \"paris\", \"color\": \"blue\", \"sport\": \"football\"}"}
Code:
SELECT JSON_VALUE(#user_json,'$.favorites.color')
Result:
NULL
I can use this trick to get values from inner JSON, but it's not clean.
Code:
SELECT JSON_VALUE(JSON_VALUE(#user_json,'$.favorites'), '$.color')
Result:
blue
How can I do this in a clean way?
Some code for testing in SQL Server:
DROP TABLE IF EXISTS #test_tbl;
DECLARE #user_json AS NVARCHAR(MAX);
SELECT 'John' AS Name, 'Duo' AS LastName, 20 AS Age, '{"city": "paris", "color": "blue", "sport": "football"}' AS favorites
INTO #test_tbl;
SET #user_json =
(
SELECT *
FROM #test_tbl
FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER
)
SELECT JSON_VALUE(#user_json,'$.favorites.color');
SELECT JSON_VALUE(JSON_VALUE(#user_json,'$.favorites'),'$.color');
You need to nest the favorites JSON using json_query(), e.g.:
SET #user_json =
(
SELECT Name, LastName, Age, json_query(favorites) as favorites
FROM #test_tbl
FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER
)
SELECT JSON_VALUE(#user_json,'$.favorites.color');
# (No column name)
# ----------------
# blue

Retrieving json elements with a specific key name from a complex nested structure in postgres

I have a complex nested json structure in a postgres json field. I want to list all element values with key '$type' no matter where in the nested structure they appear. The structure contains arrays nested within arrays to several levels deep. What is the sql query I should use?
The table structure is:
create table if not exists documents
(
id text not null
constraint documents_pkey primary key,
value json not null
)
This recursive function extracts all attributes from a complex jsonb object:
create or replace function jsonb_extract_all(jsonb_data jsonb, curr_path text[] default '{}')
returns table(path text[], value text)
language plpgsql as $$
begin
if jsonb_typeof(jsonb_data) = 'object' then
return query
select (jsonb_extract_all(val, curr_path || key)).*
from jsonb_each(jsonb_data) e(key, val);
elseif jsonb_typeof(jsonb_data) = 'array' then
return query
select (jsonb_extract_all(val, curr_path || ord::text)).*
from jsonb_array_elements(jsonb_data) with ordinality e(val, ord);
else
return query
select curr_path, jsonb_data::text;
end if;
end $$;
Example usage:
with my_table(data) as (
select
'{
"$type": "a",
"other": "x",
"nested_object": {"$type": "b"},
"array_1": [{"other": "y"}, {"$type": "c"}],
"array_2": [{"$type": "d"}, {"other": "z"}]
}'::jsonb
)
select f.*
from my_table
cross join jsonb_extract_all(data) f
where path[cardinality(path)] = '$type';
path | value
-----------------------+-------
{$type} | "a"
{array_1,2,$type} | "c"
{array_2,1,$type} | "d"
{nested_object,$type} | "b"
(4 rows)
You can use a resursive query. I have done most of the work here:
with recursive dived(jkey, jval, jtype) as (
select t.key, t.value,
json_typeof(t.value) jtype
from json_each('{"id":"243769","name":"domains","type":"TABLE","adata":{"sfield":"name"},"fields":{"id":{"ind":1,"enum":null,"refs":[null,null],"reqd":true,"type":"int4","constr":["p",null],"default":null},"name":{"ind":2,"enum":null,"refs":[null,null],"reqd":true,"type":"text","constr":["u",null],"default":null},"appid":{"ind":5,"enum":null,"refs":["apps","id"],"reqd":true,"type":"int4","constr":[null,null],"default":null},"userid":{"ind":8,"enum":null,"refs":["users","id"],"reqd":true,"type":"int8","constr":[null,null],"default":null},"createdat":{"ind":6,"enum":null,"refs":[null,null],"reqd":true,"type":"timestamptz","constr":[null,null],"default":null},"updatedat":{"ind":7,"enum":null,"refs":[null,null],"reqd":true,"type":"timestamptz","constr":[null,null],"default":null},"subdomainforward":{"ind":4,"enum":null,"refs":[null,null],"reqd":false,"type":"text","constr":[null,null],"default":null},"wilcardsubdomain":{"ind":3,"enum":null,"refs":[null,null],"reqd":false,"type":"bool","constr":[null,null],"default":null}},"schema":"web","relchecks":0,"relhasrules":false,"relhastriggers":true,"relrowsecurity":false,"relforcerowsecurity":false}'::json) t
union all
select t.key, t.value,
json_typeof(t.value) jtype
from dived, json_each(dived.jval) as t
where dived.jtype in ('object' /*, 'array'*/)
)
select * From dived where jkey = 'yourkey' limit 100
You will simply need to add in an case when or some logic when it comes to arrays and json_array_elements.
Iterating through nested arrays with json is not too difficult with a recursive query but I find it tedious.
Place the CASE WHEN in front of the json_each as something like:
CASE WHEN dived.jtype = 'array' then
json_array_elements(dived.jval) t
It may be possible to handle the situation with the case when scenario, otherwise you may need a separate recursive query specifically for arrays and then do a union with the object keys/values.
You also may find more info here:
Collect Recursive JSON Keys In Postgres
I hope this helps!

SQL - The used select statement have a different number of colums

I'm trying to make my first function, it creates without any error, but, when I try to use it it gives me error.
Here's the function -
CREATE FUNCTION isie_kontakti (condition CHAR(3))
RETURNS CHAR(100)
BEGIN
DECLARE returnthis CHAR(100);
SELECT DISTINCT Person.name, Person.lastName, Contacts.mobile, Contacts.email
FROM Person JOIN Contacts on Contacts.Person_ID = Person.ID
JOIN ParentChild on ParentChild.parentID = Person.ID
JOIN ChildGroup ON ChildGroup.Person_ID = ParentChild.childID
WHERE ChildGroup.Group_ID = 'condition' INTO returnthis;
RETURN returnthis;
END//
Table schema - http://www.imagesup.net/dm-713886347846.png
You create your function to return a single column of type char(100) yet the returnthis item contains quite a few columns.
You need to match up your query and return type.
How you do that depends on what you're trying to achieve. It's possibly as simple as just concatenating the columns from the select into a single variable, something along the lines of (untested since I don't have my DBMS available at the moment):
SELECT Person.name | ' '
| Person.lastName | ' '
| Contacts.mobile | ' '
| Contacts.email
FROM ...

SQL query to remove certain text from each field in a specific column?

I recently recoded one of my sites, and the database structure is a little bit different.
I'm trying to convert the following:
*----*----------------------------*
| id | file_name |
*----*----------------------------*
| 1 | 1288044935741310953434.jpg |
*----*----------------------------*
| 2 | 1288044935741310352357.rar |
*----*----------------------------*
Into the following:
*----*----------------------------*
| id | file_name |
*----*----------------------------*
| 1 | 1288044935741310953434 |
*----*----------------------------*
| 2 | 1288044935741310352357 |
*----*----------------------------*
I know that I could do a foreach loop with PHP, and explode the file extension off the end, and update each row that way, but that seems like way too many queries for the task.
Is there any SQL query that I could run that would allow me to remove the file exentision from each field in the file_name column?
You can use the REPLACE() function in native MySQL to do a simple string replacement.
UPDATE tbl SET file_name = REPLACE(file_name, '.jpg', '');
UPDATE tbl SET file_name = REPLACE(file_name, '.rar', '');
This should work:
UPDATE MyTable
SET file_name = SUBSTRING(file_name,1, CHAR_LENGTH(file_name)-4)
This will strip off the final extension, if any, from file_name each time it is run. It is agnostic with respect to extension (so you can have ".foo" some day) and won't harm extensionless records.
UPDATE tbl
SET file_name = TRIM(TRAILING CONCAT('.', SUBSTRING_INDEX(file_name, '.', -1) FROM file_name);
You can use SUBSTRING_INDEX function
SUBSTRING_INDEX(str,delim,count)
Where str is the string, delim is the delimiter (from which you want a substring to the left or right of), and count specifies which delimiter (in the event there are multiple occurrences of the delimiter in the string)
Example:
UPDATE table SET file_name = SUBSTRING_INDEX(file_name , '.' , 1);