简体   繁体   中英

Wrong use of joins on same table

I'm having problems to get the correct data from a table which holds different types of records, and I want to count how many of each I've got.

This is a simpler schema:

companies:

| id | name     |
|----|----------|
| 1  | company1 |
| 2  | company2 |



messages:

| id | type    | text  | companies_id |
|----|---------|-------|--------------|
| 1  | request | blah  | 1            |
| 2  | report  | blah! | 1            |
| 3  | request | foo   | 2            |
| 4  | request | bar   | 2            |
| 5  | report  | hi!   | 2            |

And a link to the SQL Fiddle:

http://sqlfiddle.com/#!9/e7237f/6

Note that if I only use one join, to get the total of just one message type for any company, it works:

SELECT c.*, COUNT(m1.id) as requests
FROM companies c 
LEFT JOIN messages m1 ON m1.companies_id = c.id AND m1.type = 'request' 
GROUP BY c.id;

It won't if I try to list the total for all the message types on any company, like I'm doing here:

SELECT c.*, COUNT(m1.id) as requests, COUNT(m2.id) as reports
FROM companies c 
LEFT JOIN messages m1 ON m1.companies_id = c.id AND m1.type = 'request' 
LEFT JOIN messages m2 ON m2.companies_id = c.id AND m2.type = 'report' 
GROUP BY c.id;

What am I doing wrong? I think there must be a way to do it using joins instead of nested queries, which I guess would be pretty slower.

LEFT JOIN only once. Use case expressions to do conditional counting

SELECT c.*,
       SUM(case when m1.type = 'request' then 1 else 0 end) as requests,
       SUM(case when m1.type = 'report' then 1 else 0 end) as reports
FROM companies c 
LEFT JOIN messages m1 ON m1.companies_id = c.id
GROUP BY c.id;

I think, there is no required for two different joins;

SELECT 
c.id, 
sum(case when m1.type = 'request' then 1 else 0 end) as requests,
sum(case when m1.type = 'report' then 1 else 0 end) as reports
FROM companies c 
LEFT JOIN messages m1 ON m1.companies_id = c.id
GROUP BY c.id;

The simplest solution would be to just count the distinct IDs:

SELECT c.*, COUNT(DISTINCT m1.id) as requests, COUNT(DISTINCT m2.id) as reports
FROM companies c 
LEFT JOIN messages m1 ON m1.companies_id = c.id AND m1.type = 'request' 
LEFT JOIN messages m2 ON m2.companies_id = c.id AND m2.type = 'report' 
GROUP BY c.id;

You could simplify your query as to get different counts based on your types with single join clause

SELECT c.*,
SUM(m.type = 'request') as requests,
SUM(m.type = 'report')  as reports
FROM companies c 
LEFT JOIN messages m ON m.companies_id = c.id
GROUP BY c.id;

In Mysql if sum is used conditional expression it will return as a boolean 0/1 based on if condition is met or not

DEMO

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM