SQL Union & join - mysql

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.

Related

how get records of a table as columns of other table for report

I have 4 tables:
users
id
name
1
a
2
b
products
id
name
1
aC
2
bC
bought
id
user_id
product_id
amount
1
1
1
100
2
2
1
200
sold
id
user_id
product_id
amount
1
1
1
100
2
2
2
200
Report
Now I want to have a query to get below report:
user_id
user_name
bought_aC
bought_bC
sold_aC
sold_bC
1
a
100
2
b
100
200
Description
I want to have list of users with bought and sold amount of products.
Each user has one row in report. each product has two column for one for bought , one for sold.
My products are limited, so I want to have different columns for each product(each product has bought and sold column that show amount)
Is this possible?
I would appreciate if any one help me.
You are aggregating along two dimensions, so you need to aggregate before joining:
select u.id, u.name,
b.bought_aC, b.bought_bC,
s.sold_aC, s.sold_bC
from users u left join
(select b.user_id,
sum(case when pb.name = 'aC' then b.amount end) as bought_aC,
sum(case when pb.name = 'bC' then b.amount end) as bought_bC
from bought b join
products pb
on b.product_id = pb.id
group by b.user_id
) b
on u.id = b.user_id left join
(select s.user_id
sum(case when ps.name = 'aC' then s.amount end) as sold_aC,
sum(case when ps.name = 'bC' then s.amount end) as sold_bC
from sold s join
products ps
on s.product_id = ps.id
group by s.user_id
) s
on u.id = s.user_id
order by u.id
I guess something like this should work. Not sure how limited your products are, but you will have to add case when statement for each product.
select u.id, u.name,
case when pb.name = 'aC' then b.amount end as bought_aC,
case when pb.name = 'bC' then b.amount end as bought_bC,
case when ps.name = 'aC' then s.amount end as sold_aC,
case when ps.name = 'bC' then s.amount end as sold_bC
from users u
left join bought b on u.id = b.user_id
join products pb on b.product_id = pb.id
left join sold s on u.id = s.user_id
join products ps on s.product_id = ps.id
order by u.id
As, you want to have an output table having all the required columns.You have to use all these but make sure to use the bought and sold table perfectly as they carry the most data from which you can get user's info as well as product info.
SELECT U.id, U.name,
B.bought_Ac, B.bought_Bc,
S.sold_Ac, S.sold_Bc
from users U LEFT JOIN
bought B where B.user_id IN (SELECT
B.user_id,sum(
CASE when P.name = 'aC' THEN B.amount end) as bought_aC,
sum(
CASE when P.name = 'bC'THEN B.amount end) as bought_bC
from bought B join
products P
on B.product_id = B.id
group by B.user_id
) B
on U.id = B.user_id left join(SELECT
S.user_id,
sum(CASE when PR.name = 'aC' THEN s.amount end) as sold_aC,
sum(case when PR.name = 'bC' THEN s.amount end) as sold_bC
from sold S join
products PR
on S.product_id = PR.id
group by S.user_id) S
on U.id = S.user_id
order by U.id

How to select customer's final balance for all customer in mysql

I have 3 tables.
table_customers - customer_id, name
table_orders - order_id, customer_id, order_datetime
table_wallet - customer_id, amount, type // type 1- credit, type 2- debit
I need to get all customers, their total balance, and their last order date and order id. This is my query.
SELECT
C.customer_id,
C.name,
COALESCE( SUM(CASE WHEN type = 2 THEN -W.amount ELSE W.amount END), 0) AS value,
COALESCE( max( O.order_id ) , '0' ) AS last_order_id,
COALESCE( max( date( O.order_datetime ) ) , '0000-00-00' ) AS last_order_date
FROM
table_customers as C
LEFT JOIN
table_wallet as W
ON C.customer_id = W.customer_id
LEFT JOIN
table_orders AS O
ON W.customer_id = O.customer_id
group by C.customer_id
ORDER BY C.customer_id
Everything is coming correct except customer's total value. From result it seems its getting added multiple times.
What is wrong in query? Can anyone help me on this?
This is doing a many-to-many join on table_customers to table_orders, which will mess with your sums. Rather do this:
SELECT C.customer_id
, C.name
, IFNULL((SELECT SUM(IF(W.type=2, -1*W.amount, W.amount))
FROM table_wallet W
WHERE C.customer_id = W.customer_id),0) AS value
, IFNULL((SELECT MAX(DATE(O.order_id))
FROM table_orders O
WHERE C.customer_id = O.customer_id),'0') AS last_order_id
, IFNULL((SELECT MAX(DATE(O.order_datetime))
FROM table_orders O
WHERE C.customer_id = O.customer_id),'0000-00-00') AS last_order_date
FROM table_customers as C
ORDER BY C.customer_id
This will return one row per customer, then subquery the fields you want. I've substituted IFNULL for COALESCE as I find it cleaner, but this is a preference thing.

MySQL - How to group sum top 6 and the rest

I have reports table that aggregates data each product quantity each day.
SELECT r.year, r.month, c.id, c.client_name, p.product_name, cc.country_name, sum(r.quantity) units FROM
client c
join report r on c.id = r.client_id
join product p on r.product_id = p.id
join country cc on r.country_id = c.id
WHERE r.year = year(now())
group by r.year, r.month, c.id, p.product_name, cc.country_name
I'm trying to figure out how group units sum by month, client, product and country where query shows sum for top 5 countries and rest is sum from bottom countries. Something like this:
case
when sum(r.quantity) = 'Top 1' then cc.country_name
when sum(r.quantity) = Top 2' then cc.country_name
.....
when sum(r.quantity) = 'Top 2' then cc.country_name
else 'Other'
How can I do this?
Many thanks in advance
you can try this. Here i sort the result from most quantity to less and add a row number. so the first 6 are the top.
please try it, but i cant tested.
SELECT
#nr := ( #nr +1) AS nr,
IF ( #nr < 7, CONCAT('Top ',#nr), 'other' ) AS top,
r.* FROM (
SELECT r.year, r.month, c.id, c.client_name, p.product_name, cc.country_name, sum(r.quantity) units
FROM CLIENT c
JOIN report r ON c.id = r.client_id
JOIN product p ON r.product_id = p.id
JOIN country cc ON r.country_id = c.id
WHERE r.year = YEAR(now())
GROUP BY r.year, r.month, c.id, p.product_name, cc.country_name
ORDER BY sum(r.quantity) DESC
) AS r
CROSS JOIN ( SELECT #nr:=0 ) AS params;

select one more table according to the condition and add it in conditions

SELECT a.id,
a.username,
a.email,
a.created,
b.user_id,
SUM(b.price) +
SUM(IF(d.beginner =0, 15, 0)) + CASE WHEN SUM(b.price) >499 THEN 100
ELSE 0 END AS points,
SUM(b.price) AS pris,
b.created,
c.user_id,
c.referrer_id,
c.created,
d.id,
d.beginner,
d.winner_id
FROM users a,
accounts b,
referrals c,
product d
WHERE a.created BETWEEN '2013-04-01' AND '2013-06-30'
AND d.created BETWEEN '2013-06-01' AND '2013-06-30'
AND b.created BETWEEN '2013-06-01' AND '2013-06-30'
AND a.id = b.user_id
AND a.id = c.user_id
AND d.winner_id = a.id
GROUP BY c.referrer_id
ORDER BY `points` DESC LIMIT 0 , 3
In this query I have four tables. With product table I want it also to be selected according to created. But when I do like what I did in the query its not giving me the correct result. I want to select product table also with this query where condition is that I need only to selects records from 1st of June to 30 June and put them according to the condition that it will add 15points if winner found in product table.
If you see this SUM( IF( d.beginner =0, 15, 0 ) ) condition it works if I don't add AND d.created
BETWEEN '2013-06-01'
AND '2013-06-30' and if users not found in product table but having points like for example 30points from rest of two conditions, it is not showing with this query. I don't know why?
I want both products and accounts table to be selected from 1st of june to 30th June and table users from 1st of April to 30th June
SELECT a.id, a.username, a.email, a.created, b.user_id, SUM( b.price ) +
CASE WHEN SUM( b.price ) >499
THEN 100
ELSE 0
END AS gr, SUM( b.price ) AS pris, b.created, c.user_id, c.referrer_id, c.created
FROM users a, accounts b, referrals c
WHERE a.created
BETWEEN '2013-04-01'
AND '2013-06-30'
AND b.created
BETWEEN '2013-06-01'
AND '2013-06-30'
AND a.id = b.user_id
AND a.id = c.user_id
GROUP BY c.referrer_id
ORDER BY `gr` DESC
LIMIT 0 , 3
I have removed products table condition now from this. So its easy now to understand. I want to add table products where select rows only from 1st Jun to 30Jun And second condition is add 15points if users found in product table in winner_id column.
Your comments suggest no record is found on the product table in that range. If you still want a row brought back in such a situation then use a LEFT OUTER JOIN. Something like as follows:-
SELECT a.id,
a.username,
a.email,
a.created,
b.user_id,
SUM(b.price) +
SUM(CASE WHEN d.beginner IS NULL THEN 0 WHEN d.beginner = 0 THEN 15 ELSE 0 END) + CASE WHEN SUM(b.price) >499 THEN 100
ELSE 0 END AS points,
SUM(b.price) AS pris,
b.created,
c.user_id,
c.referrer_id,
c.created,
d.id,
d.beginner,
d.winner_id
FROM users a
INNER JOIN accounts b ON a.id = b.user_id
INNER JOIN referrals c ON a.id = c.user_id
LEFT OUTER JOIN product d ON d.winner_id = a.id AND d.created BETWEEN '2013-06-01' AND '2013-06-30'
WHERE a.created BETWEEN '2013-04-01' AND '2013-06-30'
AND b.created BETWEEN '2013-06-01' AND '2013-06-30'
GROUP BY c.referrer_id
ORDER BY `points` DESC
LIMIT 0 , 3

MySQL - Complicated SUMs inside Query

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