MySQL - JOIN only if a row exists from the left table - mysql

Here is MySQL:
SELECT a.id,
a.name,
a.n,
a.r,
a.pot,
a.ticket_price,
a.starting_tickets,
a.started,
a.end,
COUNT(b.id) tickets_bought
FROM current_lotteries a
JOIN lottery_tickets b ON b.lid=a.id
WHERE a.cid=1
ORDER BY started DESC LIMIT 1
In the search, if there is no row from a but there are rows in b (i.e COUNT(b.id) is not NULL) then this query returns a row with NULL values for a fields and whatever the value of COUNT(b.id) as tickets_bought. How do I modify this query so it does not return a row (num_rows = 0) if there is no result in table a?
A Snap.

Absent a GROUP BY clause, MySQL (which permits this where it would be an error in other RDBMS) is applying the aggregate group over all rows in b when it should be grouping them. Add GROUP BY a.id
SELECT a.id,
a.name,
a.n,
a.r,
a.pot,
a.ticket_price,
a.starting_tickets,
a.started,
a.end,
COUNT(b.id) tickets_bought
FROM current_lotteries a
JOIN lottery_tickets b ON b.lid=a.id
WHERE a.cid=1
GROUP BY a.id
ORDER BY started DESC LIMIT 1
The above will work in MySQL but not elsewhere. A more portable version uses a correlated subquery:
SELECT a.id,
a.name,
a.n,
a.r,
a.pot,
a.ticket_price,
a.starting_tickets,
a.started,
a.end,
b.tickets_bought
FROM current_lotteries a
/* More portable to join against a subquery which returns the count per group */
JOIN (
SELECT b.lid, COUNT(*) AS tickets_bought
FROM lottery_tickets
GROUP BY lid
) b ON a.id = b.lid
WHERE a.cid = 1
ORDER BY started DESC LIMIT 1

Try this:
SELECT a.id, a.name, a.n, a.r, a.pot, a.ticket_price,
a.starting_tickets, a.started, a.end, b.tickets_bought
FROM current_lotteries a
RIGHT JOIN (SELECT b.lid, COUNT(*) AS tickets_bought
FROM lottery_tickets GROUP BY lid ) b ON a.id = b.lid
WHERE a.cid = 1
ORDER BY started DESC
LIMIT 1;

Related

How to combine aggregated queries with different joins?

I've been looking at this SO post and this jooq.org post, trying to figure out how to do my combined aggregations in MySQL, but not having much luck.
Here are my 2 queries:
select a.IsTestAgency, a.ID, a.AgencyName, a.CreateDate, count(*) AS Agents
from Users u
join Agencies a
on u.AgencyID = a.ID
group by a.IsTestAgency, a.ID, a.AgencyName, a.CreateDate
order by a.IsTestAgency, a.AgencyName;
Results:
and:
select a.IsTestAgency, a.ID, a.AgencyName, a.CreateDate, count(*) AS Certs
from Certificates c
join Agencies a
on c.AgencyID = a.ID
group by a.IsTestAgency, a.ID, a.AgencyName, a.CreateDate
order by a.IsTestAgency, a.AgencyName;
Results:
You can see that the columns and columns' datatypes match. I'd like to combine these into a single query and show the Agents count and the Certs count side-by-side, since those are the only 2 column values that are different in the result sets.
How is it done?
You could do this by JOINing to tables of COUNTs:
select a.IsTestAgency, a.ID, a.AgencyName, a.CreateDate, u.Agents, c.Certs
from Agencies a
join (select AgencyID, COUNT(*) as Agents from Users group by AgencyID) u on u.AgencyID = a.ID
join (select AgencyID, COUNT(*) as Certs from Certficates group by AgencyID) c on c.AgencyID = a.ID
order by a.IsTestAgency, a.AgencyName;
This removes the need to group by in the top query and also saves having to do two subquery counts for each row of the output.
Is this what you want?
select a.*,
(select count(*)
from users u
where u.AgencyID = a.ID
) as users_count,
(select count(*)
from Certificates c
where c.AgencyID = a.ID
) as certificates_count
from Agencies a
order by a.IsTestAgency, a.AgencyName;

Mysql Query to get latest record while using multiple joins, group by and sum()

SELECT Task_Entry_Icode, Task_Master_Icode, Work_Progress,SUM(A.Logged_Hours) as logged,B.*
FROM task_entry A INNER JOIN task_master B on A.Task_Master_Icode=B.Task_Icode
WHERE Task_Entry_Icode IN (
SELECT MAX(Task_Entry_Icode)
FROM task_entry
GROUP BY Task_Master_Icode
);
SELECT *
FROM task_master A INNER JOIN task_entry B ON A.Task_Icode=B.Task_Master_Icode
WHERE Created_On IN (
SELECT MAX(Created_On)
FROM task_entry
GROUP BY Task_Master_Icode
) AND A.Task_Created_By='7';
this works for me but if i use sum() it returns only one record
SELECT
B.Task_Entry_Icode,
B.Task_Master_Icode,
(SELECT Work_Progress FROM task_entry WHERE Task_Master_Icode=B.Task_Master_Icode ORDER BY B.Created_On DESC LIMIT 1 OFFSET 0),
SUM(B.Logged_Hours)
FROM
task_master A INNER JOIN task_entry B ON A.Task_Icode=B.Task_Master_Icode
GROUP BY
B.task_master_icode;

Group and count rows and then use them in condition

I have an query like:
SELECT * FROM account AS a
LEFT JOIN (SELECT SUM(bill.amount) total, bill.accountId FROM bill GROUP BY bill.accountId) b ON a.id = b.accountId
WHERE a.partner_id = 1 OR a.partner_id = 2
How can I check, how many groups in "bill" has the same a.partner_id?
For example: 3 groups has partner_id = 1, 2 groups has partner_id = 2.
And later include to left join only groups, if more than 2 groups have the same partner_id.
If I understand correctly, you just want an aggregation on top of your query:
SELECT a.partner_id, count(*) as cnt, sum(total) as total
FROM account a LEFT JOIN
(SELECT SUM(b.amount) as total, b.accountId
FROM bill b
GROUP BY b.accountId
) b
ON a.id = b.accountId
GROUP BY a.partner_id;
You should be able to use the "HAVING" clause. Below is an example from the following link:
https://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
SELECT name, COUNT(name) AS c FROM orders
GROUP BY name
HAVING c = 1;

MySQL IN Statement + return row with most matches

I have the following SQL Query - note: the IN words change for different queries:
SELECT a.pid,a.city,a.countryCode,b.zipEnabled,b.english
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia");
I get 3 returns.
2 match 'australia' and 1 matches 'donvale' and 'australia'.
Is there a way for me to return or order by the highest matches?
I can manipulate the results with PHP but would be great if I could do this with SQL alone.
cheers
Use limit to get the top record:
SELECT a.pid,a.city,a.countryCode,b.zipEnabled,b.english
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia")
Group by a.city Order by count(a.city) desc
limit 1 ;
Hmmm, is this what you mean?
SELECT w.countryCode, count(*)
FROM geoWorld w JOIN
geoCountry c
ON w.countryCode = c.countryCode
WHERE w.city IN ('free', 'dating', 'donvale', 'australia')
GROUP BY w.countryCode
ORDER BY count(*) DESC;
Use "group by":
select a.pid,a.city,a.countryCode, b.zipEnabled,b.english, a.c from (
SELECT a.pid,a.city,a.countryCode, count(*) c
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia")
group by a.pid,a.city,a.countryCode
) AS a JOIN geoCountry AS b ON a.countryCode=b.countryCode
order by c desc
or
SELECT a.pid,a.city,a.countryCode,b.zipEnabled,b.english,
(select count(*) from geoCountry c where c.countryCode = a.countryCode) ct
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE
a.city IN ("free","dating","donvale","australia")
order by ct desc
or if you "need to return the row that matches 2 values in the IN Statement" use having statement, e.g.:
select a.pid,a.city,a.countryCode, b.zipEnabled,b.english, a.c from (
SELECT a.pid,a.city,a.countryCode, count(*) c
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia")
group by a.pid,a.city,a.countryCode
having c=2
) AS a JOIN geoCountry AS b ON a.countryCode=b.countryCode
order by c desc

mysql order by performance optimization

Hi I like to make a perfomant order by query.
This is my query:
SELECT a.status,a.title,a.id,
b.deployed,b.accepted,b.rejected,b.error
FROM a
LEFT JOIN b ON ( a.id = b.id )
WHERE b.agencyid = 1 AND a.userid = 3
ORDER BY a.date DESC
LIMIT 0,50
without the ORDER BY clause the query takes 0.006 s
with the ORDER BY clause it needs 1.1181 s !!!
The most time consumption comes from Copying To Tmp Table 1,1 s 99,78% 1 1,1 s
How I can decrease the Copy to tpm table time?
First, as noted by Strawberry, because you have the "b" alias referenced in the where clause, and not considering NULL, it in essence turns your left join to an INNER JOIN.
SELECT
a.status,
a.title,
a.id,
b.deployed,
b.accepted,
b.rejected,
b.error
FROM a JOIN b
ON a.id = b.id
WHERE
a.userid = 3
AND b.agencyid = 1
ORDER BY
a.date DESC
LIMIT
0,50
If you meant for it to be a LEFT-JOIN (all "a" records regardless of a match in the "b" table), then you would need to move your "b" qualifier to the JOIN clause.
SELECT
a.status,
a.title,
a.id,
b.deployed,
b.accepted,
b.rejected,
b.error
FROM a LEFT JOIN b
ON a.id = b.id
AND b.agencyid = 1
WHERE
a.userid = 3
ORDER BY
a.date DESC
LIMIT
0,50
That being said, you are looking for a specific user ordered by date. I would have your "a" table indexed on (userid, date) so the engine can be optimized on your WHERE qualifier AND the order by and should resolve your performance issue regardless of left and inner join...
To help the join condition to your "b" table, index it on (id, agencyid)