When i added a left join for getting count of foreign table, its multiply my sum value of other left join table with the count, also I cant use distinct sum here as two values can be same:
SELECT c.id as company_id, SUM(ct.amount) as total_billed, count(l.id) as load_count
FROM tbl_companies c
LEFT JOIN tbl_company_transactions ct ON c.id = ct.company_id
LEFT JOIN tbl_loads l ON c.id = l.company_id
GROUP BY c.id;
You need to pre-aggregate the data:
SELECT c.id as company_id, ct.total_billed,
l.load_count
FROM tbl_companies c LEFT JOIN
(SELECT ct.company_id, SUM(ct.amount) as total_billed
FROM tbl_company_transactions ct
GROUP BY ct.company_id
) ct
ON c.id = ct.company_id LEFT JOIN
(SELECT l.company_id, COUNT(*) as load_count
FROM tbl_loads l
GROUP BY l.company_id
) l
ON c.id = l.company_id;
As you have observed, the JOIN multiplies the number of rows and affects the aggregations.
You could isolate aggregate statistics and join results afterwards.
WITH
tranStats AS (
SELECT company_id, SUM(amount) AS total_billed
FROM tbl_company_transactions
GROUP BY company_id
),
loadStats AS (
SELECT company_id, COUNT(1) AS load_count
FROM tbl_loads
GROUP BY company_id
)
SELECT id, total_billed, load_count
FROM tbl_companies c
LEFT JOIN tranStats t ON t.company_id = c.id
LEFT JOIN loadStats l ON l.company_id = c.id
Gordon's answer is more scalable but for this specific query you only need one subquery — which may also offer a performance boost since joins on the pre-aggregated data may not be able to use indexes.
SELECT c.id as company_id, SUM(ct.amount) as total_billed, l.load_count
FROM tbl_companies c
LEFT JOIN tbl_company_transactions ct ON c.id = ct.company_id
LEFT JOIN (
SELECT company_id, count(*) as load_count
FROM tbl_loads
GROUP BY company_id
) l ON c.id = l.company_id
GROUP BY c.id;
The important thing to grasp is that if you need results of an aggregate function like SUM() or COUNT(), you need to be careful when you perform more than one join with multiple rows.
Related
I have three tables, company, user and share. I want to count one company's user and share, they are not relevant.
There may be a row that has share value but not user value. so I used left join, I can get results separately, but it doesn't work together.
Here is my query:
SELECT c.name, count(u.company_id), count(s.company_id)
FROM company c
LEFT JOIN user u
ON c.id=u.company_id and u.company_id=337
WHERE u.company_id is NOT NULL
LEFT JOIN share s
ON c.id=s.id AND s.company_id=337
WHERE s.company_id is NOT NULL
You need to do at least one of the counts in a subquery. Otherwise, both counts will be the same, since you're just counting the rows in the resulting cross product.
SELECT c.name, user_count, share_count
FROM company AS c
JOIN (SELECT company_id, COUNT(*) AS user_count
FROM users
GROUP BY company_id) AS u
ON u.company_id = c.id
JOIN (SELECT company_id, COUNT(*) AS share_count
FROM share
GROUP BY company_id) AS s
ON s.company_id = c.id
WHERE c.company_id = 337
Another option is to count the distinct primary keys of the tables you're joining with:
SELECT c.name, COUNT(DISTINCT u.id) AS user_count, COUNT(DISTINCT s.id) AS share_count
FROM company AS c
JOIN users AS u on u.company_id = c.id
JOIN share AS s ON s.company_id = c.id
WHERE c.company_id = 337
Your code looks okay, except for the extra WHERE clause. However, you probably want COUNT(DISTINCT), because the two counts will return the same value:
SELECT c.name, count(distinct u.company_id), count(distinct s.company_id)
FROM company c LEFT JOIN
user u
ON c.id = u.company_id and u.company_id=337 LEFT JOIN
share s
ON c.id = s.id AND s.company_id=337
WHERE s.company_id is NOT NULL AND u.company_id IS NOT NULL;
I want to join three different queries.
Here are my queries
Query1:
SELECT u.user_name,u.first_name
FROM users u join users_cstm uc on u.id=uc.id_c
WHERE u.deleted=0?
Query2:
SELECT l.assigned_user_id,count(*) AS lead_count
FROM lead l GROUP BY l.assigned_user_id?
Query3:
SELECT l.assigned_user_id,AVG(DATEDIFF(l.date_modified,l.date_entered)) AS avgdays
FROM leads l GROUP BY l.assigned_user_id?
and so on.
This is the result I am looking for user_name, first_name,lead_count,avgdays. from three tables.
BigQuery join of three tables
I have tried my solution from the above link. But I didn't get the result.
Try below query:-
SELECT u.user_name,u.first_name,
lead_table.lead_count,lead_table.avgdays
FROM users u join users_cstm uc on u.id=uc.id_c
left join
(SELECT l.assigned_user_id,
AVG(l.date_modified) AS avgdays,
count(*) AS lead_count
FROM leads l GROUP BY l.assigned_user_id) lead_table
on u.id=lead_table.assigned_user_id
WHERE u.deleted=0;
Try this:
SELECT id,user_name,first_name,lead_count,avgdays from
(SELECT id,user_name,first_name,lead_count from
(SELECT u.id,u.user_name,u.first_name FROM users u,users_cstm uc where u.id=uc.id_c and u.deleted=0) as a
LEFT JOIN
(SELECT l.assigned_user_id,count(*) AS lead_count FROM lead l GROUP BY l.assigned_user_id) as b
on a.id = b.assigned_user_id) as a
LEFT JOIN
(SELECT l.assigned_user_id,AVG(DATEDIFF(l.date_modified,l.date_entered)) AS avgdays
FROM leads l GROUP BY l.assigned_user_id) as b
on a.id = b.assigned_user_id
I have 4 table and i want to extract: id, nume, localitate, masina_id, nr_inmatriculare, an_fabricatie, rafinarie, marca, and sum (quantity+deliver_quantity) as total_quantity group by an_fabricatie , Order by marca, and put some having clouse.
I don’t know how to make this.
My query is as bellow , but I think isn't correct.
select c.id, c.nume,c.localitate,l.masina_id, i.nr_inmatriculare, i.an_fabricatie,
i.rafinarie, m.marca from clienti c inner join livrari l on c.id = l.id inner join incarcari I on l.incarcare_id = l.livrari_id inner join masina m on i.id_marca = m.id, sum(select quantity, deliver_quantity) as total_quantity group by an_fabricatie having quantity >1000 order by marca;
Incarcari table
Id|livrari_id|id_marca|nr_inmatriculare|an_fabricatie|rafinarie|aviz_incarcare|quantity|
Livrari table
Id|masina_id|client_id|incarcare_id|deliver_quantity|aviz_livrare
Masini table
Id|numar_inmatriculare|marca|an_fabricatie|
Clienti table
Id|nume|localitate|date_add|date_upd|
SELECT c.id, c.nume, c.localitate, l.masina_id, i.nr_inmatriculare, i.an_fabricatie, i.rafinarie, m.marca, (SUM(i.quantity) + SUM(l.deliver_quantity)) AS total_quantity
FROM clienti c
INNER JOIN livrari l ON c.id = l.id
INNER JOIN incarcari i ON l.incarcare_id = i.livrari_id
INNER JOIN masini m ON i.id_marca = m.id
GROUP BY i.an_fabricatie, c.id, c.nume,c.localitate,l.masina_id, i.nr_inmatriculare, i.rafinarie, m.marca
HAVING i.quantity > 1000
ORDER BY m.marca DESC;
I have three tables, libraryitems, copies and loans.
A libraryitem hasMany copies, and a copy hasMany loans.
I'm trying to get the latest loan entry for a copy only; The query below returns all loans for a given copy.
SELECT
libraryitems.title,
copies.id,
copies.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM copies
INNER JOIN libraryitems ON copies.libraryitemid = libraryitems.id AND libraryitems.deletedAt IS NULL
LEFT OUTER JOIN loans ON copies.id = loans.copyid
WHERE copies.libraryitemid = 1
ORDER BY copies.id ASC, loans.createdAt DESC
I know there needs to be a sub select of some description in here, but struggling to get the correct syntax. How do I only return the latest, i.e MAX(loans.createdAt) row for each distinct copy? Just using group by copies.id returns the earliest, rather than latest entry.
Image example below:
in the subquery , getting maximum created time for a loan i.e. latest entry and joining back with loans to get other details.
SELECT
T.title,
T.id,
T.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM
(
SELECT C.id, C.qruuid, L.title, MAX(LN.createdAt) as maxCreatedTime
FROM Copies C
INNER JOIN libraryitems L ON C.libraryitemid = L.id
AND L.deletedAt IS NULL
LEFT OUTER JOIN loans LN ON C.id = LN.copyid
GROUP BY C.id, C.qruuid, L.title) T
JOIN loans ON T.id = loans.copyid
AND T.maxCreatedTime = loans.createdAt
A self left join on loans table will give you latest loan of a copy, you may join the query to the other tables to fetch the desired output.
select * from loans A
left outer join loans B
on A.copyid = B.copyid and A.createdAt < B.createdAt
where B.createdAt is null;
This is your query with one simple modification -- table aliases to make it clearer.
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
With this as a beginning let's think about what you need. You want the load with the latest createdAt date for each c.id. You can get this information with a subquery:
select l.copyid, max(createdAt)
from loans
group by l.copyId
Now, you just need to join this information back in:
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid LEFT JOIN
(SELECT l.copyid, max(l.createdAt) as maxca
FROM loans
GROUP BY l.copyid
) lmax
ON l.copyId = lmax.copyId and l.createdAt = lmax.maxca
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
This should give you the most recent record. And, the use of left join should keep all copies, even those that have never been leant.
I need to get the id and timestamps of table sellers and all other columns (without knowing the column names) from these results returned from this MySql statement:
SELECT * FROM sellers a
LEFT JOIN users b ON a.user_id = b.id
LEFT JOIN country_types c ON a.country_type_id = c.id
LEFT JOIN language_types d ON a.language_type_id = d.id
WHERE a.email=?
The seller id though is incorrectly set because users, country_types, and language_types all have a value id. How can I set seller_id and seller_timestamp? I tried this but it is incorrect:
SELECT a.id seller_id, a.timestamp seller_timestamp, * FROM sellers a ...
You want this:
SELECT a.id as seller_id, a.timestamp as seller_timestamp, a.*, b.*, c.*, d.*
FROM sellers a
LEFT JOIN users b ON a.user_id = b.id
LEFT JOIN country_types c ON a.country_type_id = c.id
LEFT JOIN language_types d ON a.language_type_id = d.id
WHERE a.email=?
Im not sure but try alias, for example:
a.id AS seller_id
and etc.
In joins you can't select other columns in this way:
SELECT a.id seller_id, a.timestamp seller_timestamp, * FROM sellers a...
You need to write required column names.