MySQL: How to Join to first row - mysql

I have 3 tables, Master with other two TableA and TableB, and Master has both one to many relationship with the other two tables.
What I want is to get all master records associated with only the latest record from both TableA and TableB, so I use left joins as below,
SELECT
*
FROM
Master master
LEFT JOIN (SELECT ta.* FROM TableA ta WHERE ta.masterId = master.id LIMIT 1 ORDER BY ta.id DESC ) as tableA
LEFT JOIN (SELECT tb.* FROM TableB tb WHERE tb.masterId = master.id LIMIT 1 ORDER BY tb.id DESC ) as tableB
WHERE master.status = 1
However above sql statement hit an error,
Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ta' from one of the SELECTs cannot be used in global ORDER clause
; bad SQL grammar ... Table 'ta' from one of the SELECTs cannot be used in global ORDER clause
It complains .. in global ORDER clause, but it doesn't seem like that, all order clauses are used in the subquery?
Where is wrong with my sql, it might be naive but is there a better way to achieve my requirement?
Loads of thanks.

You should fetch last record of tablea, tableb for each masterId first, then left join tablea and tableb to get all the records:
select m.*, ta.*, tb.*
from master m
left join (
select tablea.*
from tablea
join (select max(id) as id from tablea group by masterId) tmp
on tablea.id = tmp.id
) ta on ta.masterId = m.id
left join (
select tableb.*
from tableb
join (select max(id) as id from tableb group by masterId) tmp
on tableb.id = tmp.id
) tb on tb.masterId = m.id

The LIMIT 1 AND ORDER BY clauses should switch places

Related

How to remove this subquery from this SQL statement?

I have a SQL statement that does left join with a table:
select a.id, b.col1 from tableA a
left join (select col1 from tableB where date = 'xxx') b on a.id = b.id
For some application constraint (I need to use Spring JPQL query that does not permit subquery), I need to "flatten" this query to remove the subquery without changing the meaning of the query: I want to enrich tableA with a subset of tableB.
I have tried a few queries such as:
select a.id, b.col1 from tableA a
left join tableB b on a.id = b.id
where (date = 'xxx' or date is null)
But that gave me different set of answer from previous query.
How do I remove this subquery?
It can be done in multiple different ways - using cte, using joins
Using join it can be implemented as -
select a.id, b.col1 from tableA a left join tableB b on a.id = b.id and b.date = 'xxx'
using CTE it can be implemented as -
with t as
(
select col1, id from tableB where date = 'xxx'
)
select a.id, b.col1 from tableA a
left join t on a.id = t.id

How to use GROUP_CONCAT on multiple JOIN

I am currently retrieving data from multiple tables using a cus_id as the initial query. From here I have multiple tables that can have various rows of data that I would like to retrieve and use GROUP_CONCAT to return this data in a single row. At the moment, my query is returning duplicate data based on the max number of rows returned by one of the group concats.
SELECT a.id,c.x,c.y,c.z
GROUP_CONCAT(a.column_a) AS aca,
GROUP_CONCAT(a.column_b) AS acb,
GROUP_CONCAT(b.column_a) AS bca,
GROUP_CONCAT(b.column_b) AS bcb,
FROM `table_a` a
INNER JOIN `table_b` b ON a.id = b.id
INNER JOIN `table_c` c ON a.id = c.id
WHERE a.id = ?
Also, in this scenario, what is the correct join method to use. I am expecting all the fields I am requesting to have some sort of data.
Problem was resolved by using sub queries to isolate the GROUP_CONCAT requests. This allowed me to get only the data I wanted without duplicate results manipulated by other JOIN requests.
SELECT a.id,c.x,c.y,c.z
(SELECT GROUP_CONCAT(column_a) FROM table_a) AS aca,
(SELECT GROUP_CONCAT(column_b) FROM table_a) AS acb,
(SELECT GROUP_CONCAT(column_a) FROM table_b) AS bca,
(SELECT GROUP_CONCAT(column_b) FROM table_b) AS bcb,
FROM table_a a
INNER JOIN `table_c` c ON a.id = c.id
WHERE a.id = ?
Aggregate before joining. Somthing along the lines of:
select
a.*,
b.grp_a,
b.grp_b,
c.grp_x,
b.grp_y
from table_a a
join
(
select
a_id,
group_concat(a order by b_id) as grp_a,
group_concat(b order by b_id) as grp_b
from table_b
group by a_id
) b on b.a_id = a.id
join
(
select
a_id,
group_concat(x order by c_id) as grp_x,
group_concat(y order by c_id) as grp_y
from table_c
group by a_id
) c on c.a_id = a.a_id
order by a.a_id;

How to use JOINS instead of sub query

SELECT *
FROM table_a
LEFT JOIN table_b ON (table_b.id = table_a.id)
WHERE table_b.created BETWEEN ? AND ?
AND table_b.manager IN(
SELECT DISTINCT (b.id)
FROM table_b a
INNER JOIN table_b b ON a.manager = b.id
AND b.user_level > 0
AND b.id != 1
)
How can I remove the sub query and use JOINS instead in the above query
Thanks
MySQL 5.5 (and lower version)'s optimizer produces a DEPENDENT SUBQUERY for IN (SELECT ...) which means every matching row is found, IN(SELECT ...) is evaluated that makes select slow.
Simple optimization of your query is make inline view as follows:
SELECT *
FROM table_a LEFT JOIN table_b ON (table_b.id = table_a.id)
INNER JOIN (
SELECT DISTINCT b.id AS id
FROM table_b a
INNER JOIN table_b b ON a.manager = b.id
AND b.user_level > 0
AND b.id != 1
) x ON table_b.manager = x.id
WHERE table_b.created BETWEEN ? AND ?
AND x.id IS NOT NULL;
I think avobe query would produce as same as your own.
Last, DISTINCT is not a function. please use SELECT DISTINCT b.id rather SELECT DISTINCT (b.id)

mysql custom order by

I have two tables. One has some product details, the other holds various photos per product.
For my problem only the following fields are used:
tableA
id - prodName
tableB
id - prod_id - photoName
where tableA.id = tableB.prod_id
Not all products have photos (in other words not every tableA.id exists in tableB).
I am only interested in the first photo of each product (that has photos).
What query would allow me to list all products on my page, showing first the ones that have photos and at the end the ones that don't? Both sets must be alphabetical ordered based on product name.
So far i have tested the following without 100% success
Using two queries:
Query 1st: SELECT prod_id FROM tableB WHERE photoName <> '' GROUP BY prod_id ORDER BY photo_name
this will give me (with some php scripting) a list (comma seperated) of all products with photos, sorted by photo name
Query 2: SELECT ..... FROM tableA WHERE ..... ORDER BY FIND_IN_SET(id, '$myList') DESC, prodName ASC
The second query will filter the products that have/have not photos, but not sorted by product name (because the original list is ordered by photo name).
Is there a way to accomplish what i want in a single query (if possible)?
Try this query -
SELECT a.prodName FROM tableA a
LEFT JOIN tableB b
ON a.id = b.prod_id
GROUP BY
a.id
ORDER BY
IF(b.prod_id IS NULL, 1, 0), a.prodName;
try left join since not all products have photos:
SELECT * FROM tableA tA
LEFT JOIN tableB tB ON ta.id = tB.prod_id
WHERE tB.id = (SELECT MIN(id) FROM tableB tB1 WHERE tB1.prod_id = tB.prod_id)
ORDER BY prodName, photoName
if you only whant the products with photo you can use INNER JOIN instead:
SELECT * FROM tableA tA
INNER JOIN tableB tB ON ta.id = tB.prod_id
WHERE tB.id = (SELECT MIN(id) FROM tableB tB1 WHERE tB1.prod_id = tB.prod_id)
ORDER BY prodName, photoName
You can union the results.
select tableA.prod_id, tableA.prodName, tableB.photoName from tableA
inner join tableB on tableA.id = tableB.prod_id
group by tableA.id order by tableA.prodName asc
union
select tableA.prod_id, tableA.prodName, tableB.photoName
from tableA left join tableB on tableA.id = tableB.prod_id
where tableB.photoName is null order by tableA.prodName asc;

Count parent rows that have children

Table A has a number of rows, only some of which have (multiple) children in Table B. I need to count the rows in table A that have children.
At the moment I have
SELECT count(tableA.id) as count
FROM (tableA)
JOIN tableB ON tableB.tableA_id = tableA.id
Unfortunately this also counts multiple children from tableB in the count. Is there any way to prevent this?
This should work:
SELECT count(tableA.id) as count
FROM (tableA)
where id in (select tableA_id from tableB)
Or, using EXISTS:
SELECT count(tableA.id) as count
FROM (tableA)
where exists (select 1 from tableB where tableB.tableA_id = tableA.id)
You only need to put the DISTINCT keyword into your COUNT (inner join contains only rows from tableA that have children in tableB)
SELECT COUNT(DISTINCT tableA.id) AS count
FROM (tableA)
JOIN tableB ON tableB.tableA_id = tableA.id
A couple of alternatives:
SELECT count(distinct tableA.id) as `count`
FROM tableA
JOIN tableB ON tableB.tableA_id = tableA.id
or:
SELECT count(*) as `count`
FROM (tableA)
where exists
(select null from tableB where tableB.tableA_id = tableA.id)