This is going to be tough to explain.
I'm looping through my client records from tbl_customers several times a day.
SELECT c.* FROM tbl_customers c
I'm returning simply the customer's: customerid, name, phone, email
Now the weird part.
I want to append 3 more columns, after email: totalpaid, totalowed, totalbalance
BUT, Those column names don't exist anywhere.
Here is how I query each one: (as a single query)
SELECT SUM(total) AS totalpaid
FROM tbl_customers_bills
WHERE customerid = X
AND billtype = 1
SELECT SUM(total) AS totalowed
FROM tbl_customers_bills
WHERE customerid = X
AND billtype = 2
SELECT SUM(total) AS totalbalance
FROM tbl_customers_bills
WHERE customerid = X
AND billtype IN(1,2)
So, the billtype is the column that tells me whether the record is paid or not.
I am at a loss here.
How can I SUM 3 separate queries into the first query's loop?
Just join customers to bills and do the sums. To separate out totalpaid and totalowed you can use SUM(CASE or SUM(IF as wless1's answer demonstrates
SELECT c.*,
SUM(CASE WHEN billtype = 1 THEN total ELSE 0 END) totalpaid ,
SUM(CASE WHEN billtype = 2 THEN total ELSE 0 END) totalowed ,
SUM(total) AS totalbalance
FROM
tbl_customers c
LEFT JOIN tbl_customers_bills b
ON c.customerid = b.customerid
and billtype in (1,2)
GROUP BY
c.customerid
Because this is MySQL you only need to group on the PK of customer.
You could do this with a combination of GROUP, SUM, and IF
SELECT c.id, c.name, c.phone, c.email,
SUM(IF(b.billtype = 1, b.total, 0)) AS totalpaid,
SUM(IF(b.billtype = 2, b.total, 0)) AS totalowed,
SUM(IF(b.billtype = 1 OR b.billtype = 2, b.total, 0)) AS totalbalance,
FROM tbl_customers c LEFT JOIN tbl_customers_bills b ON b.customerid = c.id
GROUP BY c.id
See:
http://dev.mysql.com/doc/refman/5.0/en//group-by-functions.html
http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html
Related
I know there are a lot of topics about sql union/join but this is a special case because I would like to compare the expenses of each customer in two different stores. I'm using the "Sakila" sample database
SELECT customer.last_name AS prenom,
sum(payment.amount) AS total_payƩ
FROM payment
INNER JOIN customer ON payment.customer_id = customer.customer_id
WHERE store_id = 1
LIMIT 3
UNION ALL
SELECT customer.last_name AS prenom,
sum(payment.amount) AS total_payƩ
FROM payment
INNER JOIN customer ON payment.customer_id = customer.customer_id
WHERE store_id = 2
LIMIT 3
but have a error, i need help :(
I dont think this is really what you intend. The limit will always just give whatever happens to be the first 3 (in this case) returned. No guarantee that a person shopping in store 1 buys from store 2.
You might be best with a sum( case ) condition, something like
SELECT
c.last_name prenom,
c.customer_id,
sum( case when p.store_id = 1 then p.amount else 0 end ) Store1Paid,
sum( case when p.store_id = 2 then p.amount else 0 end ) Store2Paid
FROM
payment p
JOIN customer c
ON p.customer_id = c.customer_id
WHERE
p.store_id in( 1, 2 )
GROUP BY
c.customer_id,
c.last_name
LIMIT
3
Now, if you are more concerned with who are the highest paying customers from each respective store, then, yes a union would be more practical, but be sure to include which store they are from so you know in your result set, not just 3 people from each
SELECT
c.last_name prenom,
c.customer_id,
p.store_id,
sum( p.amount ) StorePayments
FROM
payment p
JOIN customer c
ON p.customer_id = c.customer_id
WHERE
p.store_id = 1
GROUP BY
c.customer_id,
c.last_name,
p.store_id
order by
sum( p.amount ) desc
LIMIT
3
union ALL
SELECT
c.last_name prenom,
c.customer_id,
p.store_id,
sum( p.amount ) StorePayments
FROM
payment p
JOIN customer c
ON p.customer_id = c.customer_id
WHERE
p.store_id = 2
GROUP BY
c.customer_id,
c.last_name,
p.store_id
order by
sum( p.amount ) desc
LIMIT
3
The reason I included the customer ID within all the group by clauses, what if you had 10 people all the same name "John". You would not be differentiating between the individual customer by their ID.
How do I get the total amount of money paid by each customer minus the amount collected (em_paid_to)?
table customer
cust_id INT
f_name VARCHAR
l_name VARCHAR
email VARCHAR
c_limit INT
table transaction
id INT
em_paid_by VARCHAR
em_paid_to VARCHAR
amount INT
trans_date DATE
I already tried this to get the total paid by each customer but it did not work:
SELECT C.F_NAME, C.L_NAME, COUNT(T.EM_PAID_BY), SUM(T.AMOUNT)
FROM CUSTOMER C
JOIN TRANSACTION T ON C.EMAIL = T.EM_PAID_BY;
...and this to get the total collected by each customer, still the same error and I need to get the difference between the two results.
SELECT C.F_NAME, C.L_NAME, COUNT(T.EM_PAID_TO), SUM(T.AMOUNT)
FROM CUSTOMER C
JOIN TRANSACTION T ON C.EMAIL = T.EM_PAID_TO;
What I am hoping to get is like this Old McDonald oldmcdonald#gmail.com 2000
i.e (2000 + 4000 + 1000) - (2000 + 3000) = 2000
Your existing queries generate errors because they use aggregate functions (SUM(), COUNT()) without a GROUP BY clause to list all non-aggregated columns.
To solve your requirement, one solution would be to use conditional aggregation:
recover all transactions where the customer's email appears in the em_paid_to or in the em_paid_by clause
group by customer (a sensible option is to add the customer id to the GROUP BY clause, eventhough it is not part of the results)
do conditional counts and sums, depending on whether the records was matched on em_paid_to or em_paid_by
The following query gives you detailed information (count of payments by and to, amounts paid by and to, and balance), you can pick what's relevant for you:
SELECT
c.f_name,
c.l_name,
SUM(c.email = t.em_paid_by) count_paid_by,
SUM(c.email = t.em_paid_to) count_paid_to,
SUM(CASE WHEN c.email = t.em_paid_by THEN t.amound ELSE 0 END) total_paid_by,
SUM(CASE WHEN c.email = t.em_paid_to THEN t.amound ELSE 0 END) total_paid_to,
SUM(CASE WHEN c.email = t.em_paid_by THEN t.amound ELSE -1 * t.amount END) balance
FROM
customer c
INNER JOIN transaction t
ON c.email IN (t.em_paid_by, t.em_paid_to)
GROUP BY
c.cust_id,
c.f_name,
c.l_name
;
I would unpivot the data and aggregate:
select t.email, c.fname, c.lname, sum(t.amount)
from ((select em_paid_by as email, -amount as amount
from transaction t
) union all
(select em_paid_to, amount
from transaction t
)
) t
group by email;
You can join to the customer table to get the additional customer information:
select email, sum(amount)
from cusomer c join
((select em_paid_by as email, -amount as amount
from transaction t
) union all
(select em_paid_to, amount
from transaction t
)
) t
on c.email = t.email
group by t.email, c.fname, c.lname;
I would use correlated subqueries in the SELECT clause:
select c.*,
coalesce((
select sum(amount)
from transaction t
where t.em_paid_by = c.email
), 0)
-
coalesce((
select sum(amount)
from transaction t
where t.em_paid_to = c.email
), 0) as paid_balance
from customer c
If you want more information like the count of transactions, I would use subqueries in the FROM clause:
select c.*,
p.cnt_paid,
r.cnt_received
coalesce(p.sum_paid, 0) as sum_paid,
coalesce(r.sum_received, 0) as sum_received,
coalesce(p.sum_paid, 0) - coalesce(r.sum_received, 0) as paid_balance,
p.cnt_paid + r.cnt_received as total_transactions
from customer c
left join (
select em_paid_by as email, sum(amount) as sum_paid, count(*) as cnt_paid
from transaction
group by em_paid_by
) p on p.email = c.email
left join (
select em_paid_to as email, sum(amount) as sum_received, count(*) as cnt_received
from transaction
group by em_paid_to
) r on r.email = c.email
The following statement surprisingly works, but I'm not sure joining the same table 3 times is efficient. I had to disable ONLY_FULL_GROUP_BY in order for it to work.
There are 2 tables in play. One is the main table with Distributor information, the second is a table of purchases that contains the amount, date, and id of the associated Distributor in the main table (assoc).
There are 3 things I needed. Year to date sales, which SUMS the amount of a certain Distributor's sales from the current year. Last year sales, which does the same for the previous year. Then finally just get the latest purchase date and amount.
The user needs to be able to filter by these values (lys, ytd, etc...) so joining them as variables seems like the way to go. The DB size is about 7,000 records.
SELECT
d.*,
ytd_total,
lys_total,
last_amount,
last_purchase
FROM Distributor as d
LEFT JOIN (
SELECT
assoc, SUM(amount) ytd_total
FROM purchases
WHERE db = 1 AND purchase_date >= '{$year}-01-01'
GROUP BY assoc
) AS ytd
ON ytd.assoc = d.id
LEFT JOIN (
SELECT
assoc, SUM(amount) lys_total
FROM purchases
WHERE db = 1 AND purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31'
GROUP BY assoc
) AS lys
ON lys.assoc = d.id
LEFT JOIN (
SELECT
assoc, amount last_amount, purchase_date last_purchase
FROM purchases
WHERE db = 1
GROUP BY assoc
) AS lst
ON lst.assoc = d.id
WHERE ........
You can do more work in each aggregation query. I think this is more whatyou want:
select d.*, pa.ytd_total, pa.lys_total, pa.last_purchase_date, p.amount
from distributor d left join
(select p.assoc,
sum(case when p.purchase_date >= '{$year}-01-01' then p.amount end) as ytd_total,
sum(case when p.purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31' then p.amount end) as lys_total,
max(p.purchase_date) as last_purchase_date
from purchases p
where p.db = 1
group by p.assoc
) pa left join
purchases p
on pa.assoc = p.assoc and pa.last_purchase_date = p.purchase_date;
I am learning MySQL now. And I want to know more about OUTER JOIN with subqueries. So it means i dont want to use CASE and IF
So now I have a two tables that looks like this.
customer table with c_id and c_name, order table with order_id, c_id, order_type. There are only three kind values for order_type, 'a','b','c'
Now I want to make a summary that shows the order detail for the customer.
The table should looks like this
c_name a b c
person1 1 0 2
person2 0 1 2
person3 0 0 0
This is what i have so far
SELECT c.c_id, COUNT(A), COUNT(B) , COUNT(C)
FROM customer as c
LEFT OUTER JOIN (
SELECT c_id, order_type as A FROM order
WHERE order_type = 'a')
AS first
ON first.c_id = c.c_id
LEFT OUTER JOIN (
SELECT c_id, order_type as B FROM order
WHERE order_type = 'b')
AS second
ON second.c_id = c.c_id
LEFT OUTER JOIN (
SELECT c_id, order_type as C FROM order
WHERE order_type = 'c')
AS third
ON third.c_id = c.c_id
group by c_name
You are looking for a pivot query here:
SELECT
c.c_name,
SUM(CASE WHEN o.order_type = 'a' THEN 1 ELSE 0 END) AS a,
SUM(CASE WHEN o.order_type = 'b' THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN o.order_type = 'c' THEN 1 ELSE 0 END) AS c
FROM customer c
LEFT JOIN order o
ON c.c_id = o.c_id
GROUP BY
c.c_id;
The idea here is to join your two tables together and then aggregate by customer, generating the totals for each type of order using CASE expressions.
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;