mysql custom order by - mysql

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;

Related

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;

MySQL: How to Join to first row

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

Left join is returning multiple rows from Table B corresponding to ID present in Table A

I have 2 tables in my SQL database. Let's call them Table A and Table B.
I have joined both the tables using left join on ID = AID. Table A has a field by name ID and Table B has field AID and Price.
The problem is Table B can have multiple prices for the ID present in Table A.
The requirement is - If only one price is present in Table B corresponding to ID in table A then it should return that price.
If more than one price is present in table B for an ID in table A then I should get the price as Zero/null.
Query -
SELECT DISTINCT A.ID,B.Price
from A
left join B
on A.ID = B.AID
where "some condition"
Use Count() with Over() window function to find the count of records for each ID then based on count return the price
Try this way
SELECT DISTINCT A.ID,
case when count(1) over(partition by A.ID) > 1 then NULL else B.Price end
from A
left join B
on A.ID = B.AID
For Mysql
SELECT DISTINCT a.id,
CASE
WHEN c.aid IS NULL THEN NULL
ELSE b.price
END
FROM a
LEFT JOIN b
ON a.id = b.aid
LEFT JOIN (SELECT aid
FROM b
GROUP BY aid
HAVING Count(1) = 1) c
ON a.id = c.aid
For SQL Server (should work for MySQL too). One sub-query (you can put in CTE) that shows only AIDs that have only one price:
SELECT A.ID,
B.Price
FROM A
LEFT JOIN (
SELECT AID
FROM B
GROUP BY AID
HAVING COUNT(Price) = 1
) as C
ON C.AID = A.ID
LEFT JOIN B
ON C.AID = B.AID
Use below query. It should work.
SELECT A.ID, o.Price
FROM A
OUTER APPLY
(
SELECT IIF(COUNT(B.Price)>1,NULL,MAX(B.Price)) AS Price
FROM B
WHERE B.AID = A.ID
) AS o;

MySQL Query tableA order by if has post in tableB with date of this month

I have two tables.
I want to make a query on tableA and order by tableB.
This is an accounting system.
TableA contains customers. TableB contains Payments made by customers.
I want to select * from TableA order by where he has a payment in TableB with a Date of this month first, then the rest with no payments.
SELECT
*
FROM TableA
LEFT JOIN TableB
ON TableB.customer_id = TableA.customer_id
ORDER BY COALESCE(TableB.payDate, '1900-01-01') DESC
You can outer join the two tables, group by the identifying info in TableA, and order by the max payment date in TableB, like this:
select customer.customer_id, customer.first_name, customer.last_name,
max(payment.payment_date)
from customer
left outer join payment on payment.customer_id = customer.customer_id
group by customer.customer_id, customer.first_name, customer.last_name
order by max(payment.payment_date) desc

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)