SQL: query related data from multiple tables - mysql

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

Related

Insert multiple arrays' values into tables using index

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)

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;

Search for values in table1 that match values in table2 without joins (Sql Server 2008)

I ran into a situation where I have two tables that store employee information, Table1 are the employees and table2 are 3rd party temps. These tables use a different convention for the IDs (not something I have control over).
The problem is that oftentimes these 3rd party temps become employed and there is no link between these tables. When this happens, I need to make sure they don't exists in Table2 before I create them. Right now the I just want to identify matches on DOB & Last 4, although I'm probably going to add at least first name to the criteria but right now I'd like to start somewhere.
The columns although name differently are the same (DOB = Birth Date, Code = Last 4)
CREATE TABLE Table1
([Emp_ID] int, [DOB] date, [code] varchar(10))
;
INSERT INTO Table1
([Emp_ID], [DOB], [code])
VALUES
(55556, '1966-01-15', '5454'),
(55557, '1980-03-21', '6868'),
(55558, '1985-04-26', '7979'),
(55559, '1990-10-17', '1212'),
(55560, '1992-12-30', '6767')
;
CREATE TABLE Table2
([USer_ID] int, [Birth_Date] date, [last4] varchar(10))
;
INSERT INTO Table2
([User_ID], [Birth_Date], [last4])
VALUES
(22223, '1966-01-15', '5454'),
(22224, '1980-03-21', '6868'),
(22225, '1975-07-19', '4545'),
(22226, '1988-05-24', '3434')
;
Here is what I came up with, It seems to work but I need to return the user_id from table2 that is producing this match?
SELECT *
FROM table1 t1
WHERE EXISTS (SELECT 1 FROM table2 t2 WHERE t1.DOB = t2.Birth_date)
AND EXISTS (SELECT 1 FROM table2 t2 WHERE t1.code = t2.last4)
Thanks!
Try this
Without JOINS:
SELECT t1.*,
(SELECT user_id FROM table2 t2
WHERE t1.DOB = t2.Birth_date and t1.code = t2.last4) user_id
FROM table1 t1
WHERE EXISTS (SELECT 1 FROM table2 t2
WHERE t1.DOB = t2.Birth_date and t1.code = t2.last4)
With JOINS
SELECT t1.*, t2.user_id
FROM table1 t1
inner join table2 t2 on t1.DOB = t2.Birth_date and t1.code = t2.last4
SQL DEMO

MySql - Update table using select statment from same table

I'm trying to update row in a table using values from a different row (and different columns) in the same table. Something along the lines of this, although my syntax produces no results: Here is the code (updated):
UPDATE table1 AS t1 INNER JOIN
(SELECT field_id_46,field_id_47 FROM table1 WHERE entry_id = 36) AS t2
SET t1.field_id_60 = t2.field_id_46, t1.field_id_61 = t2.field_id_47
WHERE t1.entry_id = 45;
update table as t1
inner join (
select field_id_46,field_id_47 from table where entry_id = 36) as t2
set t1.field_id_60 = t2.field_id_46,
t1.field_id_61 = t2.field_id_47
where t1.entry_id = 45
or, simply
update table as t1,
(
select field_id_46,field_id_47 from table where entry_id = 36) as t2
set t1.field_id_60 = t2.field_id_46,
t1.field_id_61 = t2.field_id_47
where t1.entry_id = 45
Adding..
Same tables, with more of one registers
UPDATE table t1
INNER JOIN table t2 ON t2.entry_id = t1.entry_id
SET t1.field_id_60 = t2.field_id_60,
t1.field_id_61 = t2.field_id_61
You can update using inner join as follow :
UPDATE table1 AS t1
INNER JOIN table1 AS t2
SET t1.field_id_60 = t2.field_id_46,
t1.field_id_61 = t2.field_id_47
WHERE t1.entry_id = 54;
I found this question very useful because I was trying to insert into a table manually while that specific database was using a hibernate_sequence table.
I used the solution from this question to mod my import script.
I had a script with many "insert into" statements one after the other and I had to set the id manually. for example:
insert into T01_REGIONS (ID, NAME) VALUES ({the next id from hibernate_sequence}, 'name1');
insert into T01_REGIONS (ID, NAME) VALUES ({the next id from hibernate_sequence}, 'name2');
..
.
So what I did is the following to work around my problem:
insert into T01_REGIONS (ID, NAME) VALUES ((select next_val from hibernate_sequence limit 1), 'name1');update hibernate_sequence as t1, (select next_val+1 as next from hibernate_sequence limit 1) as t2 set t1.next_val = t2.next;
insert into T01_REGIONS (ID, NAME) VALUES ((select next_val from hibernate_sequence limit 1), 'name2');update hibernate_sequence as t1, (select next_val+1 as next from hibernate_sequence limit 1) as t2 set t1.next_val = t2.next;
..
.
Adding the extra query at the end of each line of my sql script was easy with notepad++. I know this might be very ugly but it did work for me in order to import data to a test hibernate operated mysql database while my data was coming from an oracle hibernate operated database.
You are not need to this query
SELECT field_id_46,field_id_47 FROM table WHERE entry_id = '36'
You should just do this:
UPDATE table SET (field_id_60,field_id_61) ('coming_value','other_value') WHERE entry_id = '45';
Also you can do this with 2 different coloumns.I think you can do like this.But It might i havent got any idea.You should split this query in which language do you use.In first method you should use this query.
SELECT field_id_46,field_id_47 FROM table WHERE entry_id = '36'
And You can also return String that is coming from this data.Then you can use this returned value in update function.

How do I merge two tables in SQL?

I've got two tables:
Table 1 = idhash - username - usermail
Table 2 = idhash - views - fistseen - lastseen
Now I want to merge these tables to a new table:
Table 3 = idhash - username - usermail - firstseen - lastseen
*notice that I want to drop the views column.
I've tried with solutions that I found on google, but so far they do not seem to work.
Not all the idhash columns from table 2 have a corresponding idhash in table 1. Stiil store those 'mismatched' rows with empty username and usermail
This query will give you the results:
SELECT A.*, B.firstseen, B.lastseen
FROM [Table 1] A
LEFT JOIN [Table 2] B
ON A.idhash = B.idhash
And to insert and update the results on your [Table 3]:
INSERT INTO [Table 3](idhash, username, usermail, firstseen, lastseen)
SELECT A.*, B.firstseen, B.lastseen
FROM [Table 1] A
LEFT JOIN [Table 2] B
ON A.idhash = B.idhash
LEFT JOIN [Table 3] C
ON A.idhash = C.idhash
WHERE C.idhash IS NULL
-- For SQL Server
UPDATE T3
SET firstseen = T1.firstseen,
lastseen = T1.lastseen
FROM [Table 3] T3
INNER JOIN (SELECT A.*, B.firstseen, B.lastseen
FROM [Table 1] A
LEFT JOIN [Table 2] B
ON A.idhash = B.idhash) T1
WHERE T3.firstseen != T1.firstseen OR T3.lastseen != T1.lastseen
Here's a solution for MySQL:
CREATE TABLE table3
// First get all the rows from table2, paired with matching rows from table1
(SELECT a.idhash, b.username, b.usermail, a.firstseen, a.lastseen
FROM table2 a
LEFT JOIN table1 b
ON b.idhash = a.idhash)
// Now get the remaining rows from table1 that don't have matches
UNION ALL
(SELECT null, a.username, a.usermail, null, null
FROM table1 a
LEFT JOIN table2 b
ON b.idhash = a.idhash
WHERE b.idhash IS NULL)
If you don't want the rows from table1 that don't have corresponding rows in table2, then delete the second query in the UNION clause.
You could just brute force it with your choice of programming language. Just build a new table, query both tables, join rows programmatically however they need to be joined, insert into new table.
insert into table3
select t1.idhash, t1.username, t1.usermail, t2.firstseen,t2.lastseen
from table1 t1 left join table2 t2 on t1.idhas=t2.idhas
This should be a good start. You need to tell us what to do with your mis-matched records before we can give anything more specific.
Select
table1.idhash
,username
,usermail
,firstseen
,lastseen
From table1
left join table2
on table1.idhash = table2.idhash
Create your new table with the field types in your create statement
http://www.w3schools.com/sql/sql_create_table.asp
Then do an insert Into Table3 <yourtable> select a.f1,b.f2,c.f3 from Table1 a, Table 2 b on a.id = b.id
This is pretty close to brute force.
select coalesce(t1.idhash, t2.idhash) as idhash
, username
, usermail
, firstseen
, lastseen
into table3
from table1 t1
cross join table2 t2
try this:
INSERT INTO table3 (idhash, username, usermail, firstseen, lastseen)
SELECT a.idhash, a.username, a.usermail,
b.firstseen, b.lastseen
FROM table1 a LEFT JOIN table2 b
ON a.idhash = b.idhash
I would do this:
CREATE TABLE table3
AS
SELECT idhash, username, usermail, firstseen, lastseen
FROM Table1
NATURAL FULL OUTER JOIN Table2