MySQL Join two querys with same number of rows as results - mysql

I'm trying to Join two query results as one with no luck so far.
I tried Union but that just adds the second query result after the first query result.
Tried scipping the second query alltogether and use something like cross join but that always returned the same row data from the table in the second query.
The first query is this:
SELECT
`namelist`.`id`,
`name`.`id` AS `name_id`,
`name_item`.`content`,
`order`.`create_time`
FROM
`namelist`
LEFT JOIN `name` ON `name`.`namelist_id` = `namelist`.`id`
LEFT JOIN `name_item` ON `name_item`.`name_id` = `name`.`id`
LEFT JOIN `order` ON `namelist`.`order_id` = `order`.`id`
LEFT JOIN `items` ON `items`.`id` = `name_item`.`items_id`
WHERE
`namelist`.`order_id`=1380 AND `items`.`key`='Name'
GROUP BY `name_item`.`content`
ORDER BY `name`.`id`
The second query:
SELECT `validity`, `code`, `image` FROM `code` WHERE `code`.`order_id` = 1380 ORDER BY `id`
And as a result I would like to get something like this:
id | name_id | content | create_time | validity | code | image
--------------------------------------------------------------
1 | 1 | nameone | 2022-10-01 | somedate | 123 | 123.png
1 | 2 | nametwo | 2022-10-01 | somedate | 567 | 567.png
The querys return the same number of rows but they have no common identifier, and the reseult of the second query can not be duplicated because they have unique code colum.

SELECT id,
name_id,
content,
create_time,
validity,
code,
image
FROM
(-- first query
SELECT `namelist`.`id`,
`name`.`id` AS `name_id`,
`name_item`.`content`,
`order`.`create_time`,
ROW_NUMBER() OVER (ORDER BY `name`.`id`) AS rn
FROM `namelist`
LEFT JOIN `name` ON `name`.`namelist_id` = `namelist`.`id`
LEFT JOIN `name_item` ON `name_item`.`name_id` = `name`.`id`
LEFT JOIN `order` ON `namelist`.`order_id` = `order`.`id`
LEFT JOIN `items` ON `items`.`id` = `name_item`.`items_id`
WHERE `namelist`.`order_id`=1380
AND `items`.`key`='Name'
GROUP BY `name_item`.`content`
-- ORDER BY `name`.`id`
) AS subquery1
JOIN
(-- second query
SELECT `validity`,
`code`,
`image` ,
ROW_NUMBER() OVER (ORDER BY `id`) rn
FROM `code`
WHERE `code`.`order_id` = 1380
-- ORDER BY `id`
) AS subquery2 USING (rn)
ORDER BY rn

Related

DISTINCT on one value from a group selects

I have following sql query
select devices_device.id , devices_device.code, sss.id as "site_id", sss.name as "site_name"
from devices_device
inner join st_site_site sss on devices_device.site_id = sss.id
where devices_device.deleted = false
order by devices_device.id, devices_device.start_date
I now get a list of device id's. Some of them are the same. I want to do a distinct so I only keep the first record for every device (and due to order by on start_date that would be the most recent device record for that device)
How do I do this? If I do
select distinct devices_device.id , devices_device.code, sss.id as "site_id", sss.name as "site_name"
from devices_device
inner join st_site_site sss on devices_device.site_id = sss.id
where devices_device.deleted = false
order by devices_device.id, devices_device.start_date
nothing happens
You can use the ROW_NUMBER() window function to identify the row you want. Then filtering out the other ones is easy.
For example:
select *
from (
select
d.id, d.start_date, d.code,
s.id as "site_id", s.name as "site_name",
row_number() over(partition by d.id order by start_date desc) as rn
from devices_device d
inner join st_site_site s on d.site_id = s.id
where d.deleted = false
) x
where rn = 1
order by id, start_date
In this query the ROW_NUMBER() value will be 1 for the latest row in each device group. That's how the filtering at the end removes all other rows greater than 1.
NOTE: In case there are collisions (two rows with the same recent start_date) this query will always return a single [though random] row between them.
You should probably use a GROUP BY. Something like:
select distinct devices_device.id , devices_device.code, sss.id as "site_id",
sss.name as "site_name"
from devices_device
inner join st_site_site sss on devices_device.site_id = sss.id
where devices_device.deleted = false
group by devices_device.id
order by devices_device.start_date
You could test for the min start date
drop table if exists devices_device,st_site_site;
create table devices_device(id int,code int,site_id int,start_date date,deleted int);
create table st_site_site(id int,name varchar(10));
insert into devices_device values(1,10,1,'2020-10-01',0),(1,20,1,'2020-09-01',0);
insert into st_site_site values(1,'aaa');
select devices_device.id , devices_device.code, sss.id as "site_id", sss.name as "site_name"
from devices_device
inner join st_site_site sss on devices_device.site_id = sss.id
where devices_device.deleted = false and
devices_device.start_date = (select min(d1.start_date) from devices_device d1 where d1.id = devices_device.id)
order by devices_device.id;
+------+------+---------+-----------+
| id | code | site_id | site_name |
+------+------+---------+-----------+
| 1 | 20 | 1 | aaa |
+------+------+---------+-----------+
1 row in set (0.001 sec)

Why I can not join this query with Max date?

I have an issue with the following mySQL query where it fails when Max date is introduced as shown below.
I get the following error
Error Code: 1054. Unknown column 'order_items.ORDER_ITEM_ID' in 'where
clause'
SET #UserID = 160;
SET #OrderDateTime = '2018-11-13 09:23:45';
SELECT
order_items.ORDER_ID,
listing_region.LIST_REGION_REGION_ID,
listings.LISTING_ID,
order_items.ORDER_REQUIRED_DATE_TIME,
listings.LISTING_NICK_NAME,
order_items.ORDER_QUANTITY,
order_price.ORDER_PRICE_ID,
order_items.ORDER_PORTION_SIZE,
t.LATEST_DATE,
t.ORDER_STATUS
FROM order_status_change, order_items
INNER JOIN listings ON listings.LISTING_ID = order_items.ORDER_LISTING_ID
INNER JOIN listing_region ON listing_region.LIST_REGION_LISTING_ID = listings.LISTING_ID
INNER JOIN order_price ON order_price.ORDERP_ITEM_ID = order_items.ORDER_ITEM_ID
INNER JOIN
(
SELECT MAX(order_status_change.ORDER_STATUS_CHANGE_DATETIME) AS LATEST_DATE, order_status_change.ORDER_ITEM_ID, order_status_change.ORDER_STATUS
FROM order_status_change
WHERE order_status_change.ORDER_ITEM_ID = order_items.ORDER_ITEM_ID
) AS t ON order_status_change.ORDER_ITEM_ID = t.ORDER_ITEM_ID AND order_status_change.ORDER_STATUS_CHANGE_DATETIME = t.LATEST_DATE
WHERE ((order_items.ORDER_USER_ID = #UserID) AND DATE(order_items.ORDER_REQUIRED_DATE_TIME) = DATE(#OrderDateTime))
Any help ?
I have assumed you can join order_status_change on order_items.ID = order_status_change.ORDER_ITEM_ID
If that is valid then I think this will achieve what you are after:
SET #UserID = 160;
SET #OrderDateTime = '2018-11-13 09:23:45';
SELECT
order_items.ORDER_ID
, listing_region.LIST_REGION_REGION_ID
, listings.LISTING_ID
, order_items.ORDER_REQUIRED_DATE_TIME
, listings.LISTING_NICK_NAME
, order_items.ORDER_QUANTITY
, order_price.ORDER_PRICE_ID
, order_items.ORDER_PORTION_SIZE
, t.LATEST_DATE
, order_status_change.ORDER_STATUS
FROM order_items
INNER JOIN listings ON listings.LISTING_ID = order_items.ORDER_LISTING_ID
INNER JOIN listing_region ON listing_region.LIST_REGION_LISTING_ID = listings.LISTING_ID
INNER JOIN order_price ON order_price.ORDERP_ITEM_ID = order_items.ORDER_ITEM_ID
INNER JOIN order_status_change ON order_items.ID = order_status_change.ORDER_ITEM_ID
INNER JOIN (
SELECT
MAX( mc.ORDER_STATUS_CHANGE_DATETIME ) AS LATEST_DATE
, mc.ORDER_ITEM_ID
FROM order_status_change AS mc
GROUP BY
mc.ORDER_ITEM_ID
) AS t
ON order_status_change.ORDER_ITEM_ID = t.ORDER_ITEM_ID
AND order_status_change.ORDER_STATUS_CHANGE_DATETIME = t.LATEST_DATE
WHERE order_items.ORDER_USER_ID = #UserID
AND DATE( order_items.ORDER_REQUIRED_DATE_TIME ) = DATE( #OrderDateTime )
You need to avoid this in future:
FROM order_status_change , order_items
That comma between the 2 table names IS a join, but it is from an older syntax and it is LOWER in precedence than the other joins of your query. Also, by default this comma based join acts as an equivalent to a cross join which MULTIPLIES the number of rows. In brief, please do NOT USE commas between table names.
The other issue is that you were missing a group by clause and I believe you just want to get the "latest" date from this aggregation, once that is determined link back to that table to get the status relevant to that date. (i.e. you can't group by status in the subquery, otherwise you get the latest dateS (one for each status).
Here's a simplified version to illustrate the problem.
DROP TABLE IF exists t,t1;
create table t (id int);
create table t1(id int,dt date);
insert into t values (1),(2);
insert into t1 values (1,'2018-01-01'),(1,'2018-02-01'),(2,'2018-01-01');
select t.*,t2.maxdt
from t
join (select max(dt) maxdt,t1.id from t1 where t1.id = t.id) t2
on t2.id = t.id;
ERROR 1054 (42S22): Unknown column 't.id' in 'where clause'
You could group by in the sub query and then the on clause will come into play
select t.*,t2.maxdt
from t
join (select max(dt) maxdt,t1.id from t1 group by t1.id) t2
on t2.id = t.id;
+------+------------+
| id | maxdt |
+------+------------+
| 1 | 2018-02-01 |
| 2 | 2018-01-01 |
+------+------------+
2 rows in set (0.00 sec)
If you want an answer closer to your problem please add sample data and expected output to the question as text of to sqlfiddle.

Using parent id in child query?

SELECT li_1.carrier, li_1.product_id, li_1.quantity, products_description.products_name, sites.sites_id, sites.sites_name, counted_table.counted
FROM inventory li_1
INNER JOIN products_description ON li_1.product_id = products_description.products_id
INNER JOIN sites ON products_description.data = sites.data
INNER JOIN (
SELECT SUM( li_2.quantity ) AS counted
FROM inventory li_2
WHERE li_1.product_id = li_2.product_id
) counted_table
GROUP BY li_1.product_id
ORDER BY li_1.id DESC
I'm attempting to use the parent id (product_id) to count the total amount of quantity for each product in the subquery - But I only get the standard mysql error message.
So something like
id | quantity | total
---------------------------------
0001 | 2 | 6
| |
0001 | 4 | 6
What could be wrong?
if you change sub-query, it could be fixed
SELECT li_1.carrier, li_1.product_id, li_1.quantity, products_description.products_name, sites.sites_id, sites.sites_name, counted_table.counted
FROM inventory li_1
INNER JOIN products_description ON li_1.product_id = products_description.products_id
INNER JOIN sites ON products_description.data = sites.data
INNER JOIN (
SELECT product_id,SUM( li_2.quantity ) AS counted
FROM inventory li_2
GROUP BY li_2.product_id
) counted_table ON li_1.product_id = counted_table.product_id
ORDER BY li_1.id DESC

Mysql nested select with multiple joins with condition on join table

I've got a SELECT with multiple JOINS for a paginated Tableview. In general this is working for unfiltered results.
The query looks like this:
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM
( SELECT * FROM selecttable
WHERE value = 99
ORDER BY datetime DESC
LIMIT 50 OFFSET 0 )
AS seltable
LEFT JOIN table1 AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN table2 AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN table3 AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN table4 AS tbl4 ON tbl3.tbl4_uid = tbl4.uid;
Now I've got no clue how to accomplish filtering the results with a condition related to one of the join tables.
When I just set a:
LEFT JOIN tablex AS table ON foreign_table.tblx_uid = table.uid AND {condition}
this condition regards only to the 50 results of the nested SELECT.
Is there any way to achieve using WHERE clauses on the JOIN tables in this scenario?
For sample data see http://sqlfiddle.com/#!2/fad4d/2
Expected results:
to get x team records limited to 5 team uids, where Tournament2 is one of the related tournaments for the team.
Best regards
w1ll1
Try not controlling the pagination in that subquery, instead just use a more conventional query with a composite where clause. HOWEVER, because you are using left joins take care adding filters through the where clause that would override the outer join to produce the effect of an inner join.
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM selecttable AS seltable
LEFT JOIN table1 AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN table2 AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN table3 AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN table4 AS tbl4 ON tbl3.tbl4_uid = tbl4.uid
WHERE seltable.value = 99
...
ORDER BY seltable.datetime DESC
LIMIT 50 OFFSET 0
Alternatively use more subqueries, like this:
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM
( SELECT * FROM selecttable
WHERE value = 99
ORDER BY datetime DESC
LIMIT 50 OFFSET 0 )
AS seltable
LEFT JOIN ( SELECT uid, name
FROM table1
WHERE 1=1 -- amend to suit
) AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN ( SELECT uid, name
FROM table2
WHERE 1=1 -- amend to suit
) AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN ( SELECT uid, name
FROM table3
WHERE 1=1 -- amend to suit
) AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN ( SELECT uid, name
FROM table4
WHERE 1=1 -- amend to suit
) AS tbl4 ON tbl3.tbl4_uid = tbl4.uid;
Here is another attempt, based on your sqlfiddle it appears that INNER JOINS may be used:
SELECT theteam.*,
trnmnt.name AS tournamentname,
cat.name AS categoryname,
sport.name AS sportname
FROM (
SELECT * FROM team
ORDER BY team.name ASC )
AS theteam
INNER JOIN tournament_team AS tntm ON tntm.team_uid = theteam.uid
INNER JOIN tournament AS trnmnt ON tntm.tournament_uid = trnmnt.uid AND trnmnt.name = 'Tournament2'
INNER JOIN category AS cat ON trnmnt.category_uid = cat.uid
INNER JOIN sport ON cat.sport_uid = sport.uid
LIMIT 5 OFFSET 0
;
The result of that query is:
| UID | NAME | TOURNAMENTNAME | CATEGORYNAME | SPORTNAME |
|-----|--------|----------------|--------------|-----------|
| 2 | Team02 | Tournament2 | Germany | Soccer |
| 3 | Team03 | Tournament2 | Germany | Soccer |
| 4 | Team04 | Tournament2 | Germany | Soccer |
| 5 | Team05 | Tournament2 | Germany | Soccer |
| 6 | Team06 | Tournament2 | Germany | Soccer |

Rows with null value for group_concat not returned

I've got the following MySQL query that's supposed to return records from table a and b (one to many relationship), and also a comma seperated list of any values returned from table c. However, there won't always be records in table c (which is why I'm using a LEFT OUTER JOIN to join it to table a).
SELECT `a`.`id` , `a`.`name` , `b`.`id` AS `b_id` , `b`.`name` AS `b_name` , GROUP_CONCAT( `c`.`l_id` ) AS `c_ls`
FROM `a`
INNER JOIN `b` ON `a`.`b_id` = `b`.`id`
LEFT OUTER JOIN `c` ON `a`.`id` = `c`.`a_id`
GROUP BY `a`.`id`
ORDER BY `a`.`created` DESC
The query above returns 1 record when it should return 2. Record 1 has 3 matching records in table c, record 2 has 0 matching records in table c.
The query returns:
id | name | b_id | b_name | c_ls
1 | John | 2 | Bla | [BLOB - 3 B]
If I remove the GROUP_CONCAT and GROUP_BY clauses then it returns 2 records:
id | name | b_id | b_name | c_ls
1 | John | 2 | Bla | [BLOB - 3 B]
2 | Fred | 3 | Blo | [BLOB - NULL]
It seems that if c_ls is null then GROUP_CONCAT stops the row from being returned. Any thoughts as to what I'm doing wrong?
The answer previously marked as right is unfortunately wrong (as user desaivv noted in the comment).
It must read IFNULL, [not ISNULL, isnull just takes one parameter and returns a boolean] !
IFNULL returns the second paramter if null:
SELECT `a`.`id` , `a`.`name` , `b`.`id` AS `b_id` , `b`.`name` AS `b_name` ,
IFNULL(GROUP_CONCAT( `c`.`l_id` ), '') AS `c_ls`
FROM `a`
INNER JOIN `b` ON `a`.`b_id` = `b`.`id`
LEFT OUTER JOIN `c` ON `a`.`id` = `c`.`a_id`
GROUP BY `a`.`id`
ORDER BY `a`.`created` DESC
But this is not the solution at all!
What we need is a "convoluted" join -
So please check this SQL Fiddle:
http://www.sqlfiddle.com/#!2/54c6f/3/0
Try to use LEFT JOIN instead of INNER JOIN
Try this -
SELECT `a`.`id` , `a`.`name` , `b`.`id` AS `b_id` , `b`.`name` AS `b_name` , ISNULL(GROUP_CONCAT( `c`.`l_id` ), '') AS `c_ls`
FROM `a`
INNER JOIN `b` ON `a`.`b_id` = `b`.`id`
LEFT OUTER JOIN `c` ON `a`.`id` = `c`.`a_id`
GROUP BY `a`.`id`
ORDER BY `a`.`created` DESC
Added ISNULL to check if Group_concat is null and return blank instead of NULL.