Insert multiple arrays' values into tables using index - json

Cont. with this post, a function is created to parse json input then insert values into three tables, with previous inserted ids as parameter for last
insert.
If i want to insert two arrays into the same table, i can just do
insert into t2 (car, car_type)
select json_array_elements_text(d::json -> 'car'),json_array_elements_text(d::json -> 'car_type')::int4 returning id;
)
how to make it work with index as below?
function:
create or replace function test_func(d json)
returns void as $$
begin
with j as (select d)
, a as (
select car,brand,type, t1.id oid
from j
join json_array_elements_text(j.d->'cars') with ordinality t1(car,id) on true
join json_array_elements_text(j.d->'brands') with ordinality t2(brand,id)
on t1.id = t2.id
join json_array_elements_text(j.d->'car_type') with ordinality t2(type,id)
on t1.id = t2.id // this line apparently doesnt work, t2 has been joined twice
)
, n as (
insert into t1 (name) values (d::json -> 'name') returning id
), c as (
insert into t2 (cars,car_type) select car,type from a order by oid returning id // needs to insert two columns here from two arrays
)
, ag as (
select array_agg(c.id) cid from c
)
insert into t3 (id, name_id, cars_id, brand)
select 1, n.id,cid[oid], brand
from a
join n on true
join ag on true
;
end;
$$ language plpgsql;
Tables:
CREATE TABLE t1 ( "id" SERIAL PRIMARY KEY, "name" text NOT NULL );
CREATE TABLE t2 ( "id" SERIAL PRIMARY KEY, "cars" text NOT NULL, "car_type" int );
CREATE TABLE t3 ( "id" int, "name_id" int REFERENCES t1(id), "cars_id" int REFERENCES t2(id), "brand" text );
Test:
select test_func('{"name":"john", "cars":["bmw X5 xdrive","volvo v90 rdesign"], "brands":["bmw","volvo"],"car_type":[1,1]}');

you used t2 for aliasing two different sets - try:
create or replace function test_func(d json)
returns void as $$
begin
with j as (select d)
, a as (
select car,brand,car_type, t1.id oid
from j
join json_array_elements_text(j.d->'cars') with ordinality t1(car,id) on true
join json_array_elements_text(j.d->'brands') with ordinality t2(brand,id)
on t1.id = t2.id
join json_array_elements_text(j.d->'car_type') with ordinality car_t(car_type,id)
on t1.id = car_t.id
)
, n as (
insert into t1 (name) values (d::json -> 'name') returning id
), c as (
insert into t2 (cars,car_type) select car,car_type::int from a order by oid returning id -- needs to insert two columns here from two arrays
)
, ag as (
select array_agg(c.id) cid from c
)
insert into t3 (id, name_id, cars_id, brand)
select 1, n.id,cid[oid], brand
from a
join n on true
join ag on true
;
end;
$$ language plpgsql;
result:
t=# select * from t2;
id | cars | car_type
----+-------------------+----------
1 | bmw X5 xdrive | 1
2 | volvo v90 rdesign | 1
(2 rows)

Related

Update Mysql Column of JSON array via SQL

I am storing a list of integers as JSON array inside the column called ConvertedIds inside a table SelectionLogs
The type of column is MediumText and some of the example values of the column are [290766,319075,234525,325364,3472,34241,85643,11344556,88723,656378]
I am using following sql to generate the list of IDs from the column as rows
SELECT hm.id FROM SelectionLogs hlog,
JSON_TABLE(ConvertedIds, '$[*]' columns (Id int path '$')) AS hm
And then following query to extract further informations from other tables like
SELECT hm.id,hc.firstname ,hc.lastname ,hc.email FROM SelectionLogs hlog,
JSON_TABLE(ConvertedIds, '$[*]' columns (Id int path '$')) AS hm
LEFT JOIN contacts hc ON hc.Id = hm.id
Now i have to update this column based on the presence of a given value of IDs
For example if an ID exists in this column on any rows , i have to update the array after removing the ID
For example: [1,2,3,4,5,6,7] If ID : 3 exists , remove 3 and update column as [1,2,4,5,6,7]
I can use the following query to find the records from table SelectionLogs with given id present in column ConvertedIds
SELECT DISTINCT hlog.Id FROM SelectionLogs hlog,
JSON_TABLE(ConvertedIds, '$[*]' columns (Id int path '$')) AS hm
WHERE hm.id=427529
Now i have plans to iterate through each rows from my console program written in c#
foreach row in result
List<int> columnIds = read from column ConvertedIds as list of int
Remove the given int number from List
Update column ConvertedIds for given rowId refreshed List
Can i perform the updation via SQL itself ?
DEMO fiddle with some explanations.
-- source data
CREATE TABLE t1 (id INT, val JSON)
SELECT 1 id, '[1,2,3]' val UNION SELECT 2, '[3,4,5]' UNION SELECT 3, '[5,6,7]';
CREATE TABLE t2 (id INT) SELECT 1 id UNION SELECT 4;
-- UPDATE source table
UPDATE t1
JOIN ( SELECT t1.id, JSON_ARRAYAGG(jsontable.id) val
FROM t1
CROSS JOIN JSON_TABLE(t1.val,
'$[*]' COLUMNS (id INT PATH '$')) jsontable
LEFT JOIN t2 ON t2.id = jsontable.id
WHERE t2.id IS NULL
GROUP BY t1.id ) data_for_update USING (id)
SET t1.val = data_for_update.val;

mySQL query for two disjoint tables

Is there a way to combine two SELECT statements on two disjoint tables (t1, t1) into once SELECT statement? The ideal query statement would return results from both tables when successful or the results if only the t1 query part is successful or the results if only the t2 query part is successful or nothing if the query on t1 and t2 is unsuccessful.
MySQL UNION doesn't work because the tables are disjoint. JOIN doesn't appear to work because if the query fails for one table the entire query fails.
Test case:
create table t1 (
c11 varchar(2),
c12 varchar(2),
c13 varchar(2),
c14 varchar(2),
primary key (c11));
create table t2 (
c21 varchar(2),
c22 varchar(2),
c23 varchar(2),
primary key(c21));
insert into t1 values ('a', 'b', 'c', 'd');
insert into t2 values ('x', 'y', 'z');
Example of what the two distinct SELECT statements:
SELECT c11, c12, c13, c14 from t1 where c11 = 'a'
Returns a, b, c, d
SELECT c21, c22, c23 from t2 where c21 = 'x'
Returns x, y, z
Examples of what I am trying to achieve:
SELECT * (successful query of t1 and t2) where t1.c11 = 'a' and t2.c21 = 'x'
Returns a, b, c, d, x, y, z
SELECT * (successful query of only t1 and not t2) where t1.c11 = 'a' and t2.c21 = 'v'
Returns a, b, c, d
SELECT (successful query of only t2 and not t1) where t1.c11 = 'd' and t2.c21 = 'x'
Returns x, y, z
SELECT (unsuccessful query of both t1 and t2) where t1.c11 = 'd' and t2.c21 = 'v'
Empty set.
You can just join the tables on a condition which always evaluates true, e.g.
select c11,c12,c13,c14,c21,c22,c23 from t1
left join t2 on true
where c21='x' and c11='a';
Result:
Kinda hard for me to understand why you want to do this, but there you go. Note that, without the where clause, this query will give as many results as there are rows in t1 multiplied by the number of rows in t2. So if they both have 10 rows, an unconditional query would return 100 results.

SQL: query related data from multiple tables

I have 4 tables, who all relate to each other in such a way that:
TB1 <- TB2 <- TB3 <- TB4
meaning that TB4 holds elements that belongs to a single row in TB3, TB3 holds elements that belongs to a single row in TB2 and finally TB2 holds data that belongs to a single row in TB1.
i made this illustration to try to make it more clear
(edit: DB was suppose to be TB as in table)
I have tried to achieve this by using subqueries as follows:
SELECT TB1.id AS TB1_ID,
(SELECT TB2.id AS TB2_ID,
(SELECT TB3.id AS TB3_ID,
(SELECT TB4.id AS TB4_ID
FROM `TB4` AS TB4 WHERE TB4.TB3_id = TB3.id) AS C
FROM `TB3` AS TB3 WHERE TB3.TB2_id = TB2.id) AS B
FROM `TB2` AS TB2 WHERE TB2.TB1_id = TB1.id) AS A
FROM `TB1` AS TB1
yet my logic must be flawed: or there is something i am missing about querying related data: as this returns null, even though i know that the tables holds the necessary informations needed to make such a cross combination.
The desired result is a set of nested arrays within an array: one nested array for each tables. so that we ends up with a structure like:
{*, A{*, B{*, C{*} } } }
so that each row from TB1 contains a multidimensional array of elements from TB2 as a variable and each row form TB2 contains a multidimensional array of elements from TB3 as an element and so on...
I have also tried to pull all information as separate queries and then joining them in JS, however turned out to be quit heavy: so i would truly appreciate if anyone knew how to do this in a proper way - thanks a lot in advance
PS. im trying it in my local environment, through use of XAMPP: does this create a problem ?
I think what you want is a series of JOINs:
SELECT TB1.id AS TB1_ID, TB2.id AS TB2_ID, TB3.id AS TB3_ID, TB4.id AS TB4_ID
FROM TB1
JOIN TB2 ON TB2.TB1_ID = TB1.ID
JOIN TB3 ON TB3.TB2_ID = TB2.ID
JOIN TB4 ON TB4.TB3_ID = TB3.ID
You can then build your desired structure in PHP using something like:
$sql = "SELECT TB1.id AS TB1_ID, TB2.id AS TB2_ID, TB3.id AS TB3_ID, TB4.id AS TB4_ID
FROM TB1
JOIN TB2 ON TB2.TB1_ID = TB1.ID
JOIN TB3 ON TB3.TB2_ID = TB2.ID
JOIN TB4 ON TB4.TB3_ID = TB3.ID";
$result = $conn->query($sql) or die($conn->error);
$output = array();
while ($row = $result->fetch_assoc()) {
$tb1_id = $row['TB1_ID'];
$tb2_id = $row['TB2_ID'];
$tb3_id = $row['TB3_ID'];
$tb4_id = $row['TB4_ID'];
if (isset($output[$tb1_id][$tb2_id][$tb3_id])) {
$output[$tb1_id][$tb2_id][$tb3_id][$tb4_id] = array();
}
elseif (isset($output[$tb1_id][$tb2_id])) {
$output[$tb1_id][$tb2_id][$tb3_id] = array($tb4_id => array());
}
elseif (isset($output[$tb1_id])) {
$output[$tb1_id][$tb2_id] = array($tb3_id => array($tb4_id => array()));
}
else {
$output[$tb1_id] = array($tb2_id => array($tb3_id => array($tb4_id => array())));
}
}
Here is an attempt that uses JSON functions and other stuff that should work in the latest 5.7 version.
But don't ask about it, because I discovered from this experiment that dealing with nested json's in MySql 5.7 is a real PITA.
Sample data:
drop table if exists Table1;
drop table if exists Table2;
drop table if exists Table3;
drop table if exists Table4;
create table Table1 (id int primary key, col1 varchar(30));
create table Table2 (id int primary key, tbl1_id int, col1 varchar(30));
create table Table3 (id int primary key, tbl2_id int, col1 varchar(30));
create table Table4 (id int primary key, tbl3_id int, col1 varchar(30));
insert into Table1 (id, col1) values
(101, 'A1'),(102, 'A2'),(103, 'A3'),(104, 'A4');
insert into Table2 (id, tbl1_id, col1) values
(201, 101, 'B1'), (202, 102, 'B2'),(203, 103, 'B3');
insert into Table3 (id, tbl2_id, col1) values
(301, 201, 'C1'),(302, 202, 'C2');
insert into Table4 (id, tbl3_id, col1) values
(401, 301, 'D1'), (402, 301, 'D2');
Query:
SELECT t1.id AS t1id,
GROUP_CONCAT(REPLACE(JSON_OBJECT(t1.id, JSON_ARRAY(t1.col1)),']}',', '),
IFNULL(
(
SELECT
GROUP_CONCAT(
REPLACE(JSON_OBJECT(t2.id, JSON_ARRAY(t2.col1)),']}',', '),
IFNULL(
(
SELECT
GROUP_CONCAT(
REPLACE(JSON_OBJECT(t3.id, JSON_ARRAY(t3.col1)),']}',', '),
IFNULL(
(
SELECT
CONCAT('[',
IFNULL(GROUP_CONCAT(JSON_OBJECT(t4.id, JSON_ARRAY(t4.col1))),''),
']') D
FROM Table4 t4
WHERE t4.tbl3_id = t3.id
GROUP BY t4.tbl3_id
), '[]'), ']}') C
FROM Table3 t3
WHERE t3.tbl2_id = t2.id
GROUP BY t3.tbl2_id
), '[]'), ']}') B
FROM Table2 t2
WHERE t2.tbl1_id = t1.id
GROUP BY t2.tbl1_id
), '[]'), ']}') A
FROM Table1 t1
GROUP BY t1.id;
Returns:
id A
101 {"101": ["A1", {"201": ["B1", {"301": ["C1", [{"401": ["D1"]},{"402": ["D2"]}]]}]}]}
102 {"102": ["A2", {"202": ["B2", {"302": ["C2", []]}]}]}
103 {"103": ["A3", {"203": ["B3", []]}]}
104 {"104": ["A4", []]}
A test on db<>fiddle here

MySQL subquery using INSTR

The query below will select all of the name records from table 1, which are NOT IN table 2:
SELECT t1.name
FROM t1
WHERE t1.name NOT IN (SELECT t2.name from t2)
This works fine for complete matches of the name field, but it does not work for partial matches. How can I modify the query so that it will select only the name records from table 1 where no part of the name appeals in table 2?
I suspect that the answer is going to involve using INSTR, but I'm not sure of the syntax. Thanks.
JOIN will do the job
CREATE TABLE test1 (
name VARCHAR(10);
)
CREATE TABLE test2 (
name VARCHAR(10);
)
INSERT INTO test1 VALUES
( 'x123' ),
( 'y123' ),
( 'z123' );
INSERT INTO test2 VALUES
( '123' ),
( '423' ),
( '23' );
SELECT t1.name, t2.name
FROM test1 t1
JOIN test2 t2
ON INSTR(t1.name, t2.name ) = 0
ORDER BY t1.name;

Update a column in a table

I need to update a column in a table which is of int data type but the values from other table i used for updating is nvarchar.
wheather this is achieved if so please let me know.
It could be something like this:
update tablename
set t.column = convert(int, t2.column)
from tablename t
inner join secondtablename t2 on t.column = t2.column
where ISNUMERIC(t2.column) = 1
If all you data in the nvarchar column are numbers, then, you should be able to do:
update ATable
set intColumn = cast(o.chardata as int)
from ATable a
join OtherTable o on a.tableid=o.tableid
now, you can also put in logic to handle non numeric data with an ISNUMERIC() constraint.
I see aF beat me to the punch.
CREATE TABLE #t
(
ID int IDENTITY(1,1),
Column1 int
)
CREATE TABLE #t1
(
ID int IDENTITY(1,1),
Column2 Varchar(50)
)
INSERT INTO #t(Column1)VALUES(1)
INSERT INTO #t(Column1)VALUES(2)
INSERT into #t1(Column2)values('Alpha Numeric')
INSERT into #t1(Column2)values('12')
UPDATE t
SET t.Column1 = t1.Column2
FROM #t t
INNER join #t1 t1 on t.ID = t1.ID
Where ISNUMERIC(t1.Column2) = 1
select * FROM #t
DROP TABLE #t
DROP TABLE #t1