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.
Related
I have this sql:
SELECT
c.id,
c.name,
SUM(CASE WHEN o.status = 1 THEN 1 ELSE 0 END)
FROM orders o, customers c
WHERE o.customer_id = c.id
GROUP BY c.id;
Take all customers from table customer.
Count how much rows have column status=1 in table orders, grouped by customer_id.
My sql works perfect if customer have at least one order in table orders(any status, even if all have status different by 1).
But if there is an customer which have no order(does not depend status) in table order, it will not appear in result.
Let's say we have those 2 customers:
100 - C1 - 15 orders(7 have status 1)
101 - C2 - No orders
The Result will be:
100 | C1 | 7
How I can include C2?
100 | C1 | 7
101 | C2 | 0
Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax.
And, you want a LEFT JOIN:
SELECT c.id, c.name,
COUNT(o.customer_id)
FROM customers c LEFT JOIN
orders o
ON o.customer_id = c.id AND o.status = 1
GROUP BY c.id;
You need a left join:
SELECT id, name, COALESCE(sum_orders, 0)
FROM customer c
LEFT JOIN (SELECT customer_id, SUM(CASE WHEN o.status = 1 THEN 1 ELSE 0 END) AS sum_orders
FROM orders
GROUP BY customer_id) o ON c.id = o.customer_id
I have to 3 tables: product, product_to_store, store
product table
id quantity status
1 1 1
2 0 1
3 0 1
4 23 1
product_to_store table
store_id product_id
1 1
2 2
1 3
2 4
store table
id name
1 store1
2 store2
To find total products I can run query to fetch all products in table product where status of product is enabled.
select count(*) from product where status=1
total name
2 Store 1
2 store 2
To find total out of stock products I can run below query after joining all 3 tables and using group by store_id:
Select count(*) as outofproducts from product where quantity=0;
Result come like this:
outofproducts name
1 Store 1
1 store 2
But I want combination of above 2 results in single query like below:
outofproducts total name
1 2 Store 1
1 2 store 2
You'd use conditional aggregatiopn, i.e. sum/count over conditions:
select
s.name,
sum(p.quantity > 0) as in_stock,
sum(p.quantity = 0) as out_of_stock,
count(*) as total
from store s
join product_to_store ps on ps.store_id = s.id
join product p on p.id = ps.product_id
group by s.name
order by s.name;
This makes use of MySQL's true = 1, false = 0. If you don't like it, replace sum(p.quantity = 0) with sum(case when p.quantity = 0 then 1 else 0 end) or count(case when p.quantity = 0 then 1 end).
You can start query from store table so that we will get total rows as store table data.
Then use nested query for each store to get out of product and total product count
select
(Select count(*) as outofproducts from product_to_store ps inner join product p on p.id = ps.product_id where quantity=0 and ps.store_id = s.id ) as outofproducts ,
(Select count(*) as count from product_to_store ps inner join product p on p.id = ps.product_id where ps.store_id = s.id ) as totalCount,
s.name
from store s
You could join the related subquery for count
select t1.name, t1.outofproducts, t2.total
from(
select b.id, b.name , count(*) outofproducts
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
where a.quantity = 0
group by b.id, b.name
) t1
inner join (
select b.id, b.name , count(*) total
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
group by b.id, b.name
) t2 on t1.id = t2.id
I have a problem in MYSQL query.
I have three tables one is voucher table other clients and third is voucher_client.
In voucher_client table I have voucher id column that relate to voucher table and I want to count related rows from client table.
Like if voucher table has id 2 and voucher clients are 2 then query will check from client table age_group column where age_group is adult ,child or infant
here some pictures of tables for more detail.
Please help me out
Voucher table
Client table
Voucher client table
I am trying to do like this
SELECT `v`.*, `a`.`name` as `agent_name`, COUNT(CASE WHEN c.age_group = 'Adult' THEN c.id END) AS t_adult, COUNT(CASE WHEN c.age_group = 'Child' THEN c.id END) AS t_child, COUNT(CASE WHEN c.age_group = 'Infant' THEN c.id END) AS t_infant, COUNT(c.id) as total FROM `voucher` `v` JOIN `voucher_client` `vc` ON `vc`.`voucher_id`=`v`.`id` JOIN `client` `c` ON `vc`.`client_id`=`c`.`id` JOIN `tbl_users` `a` ON `a`.`userId`=`v`.`agent_id` LEFT JOIN `voucher_hotel` `vh` ON `vh`.`voucher_id`=`v`.`id` WHERE `v`.`isDeleted` =0 GROUP BY `v`.`id` ORDER BY `v`.`id` DESC
expected output like this
voucher_id t_adult t_child t_infant
1 2 0 0
2 1 0 0
If only want to show v.id in the result, then replace v.* by v.id in the query.
(Btw, most databases wouldn't even allow a * when there's group by. MySql deviates from the ANSI SQL standard in that aspect.)
And if you need to join to an extra table with an 1-N relationship? Then you can count the distinct values. So that the totals only reflect the unique clientid's.
SELECT
v.id AS voucher_id,
COUNT(DISTINCT CASE WHEN c.age_group = 'Adult' THEN c.id END) AS t_adult,
COUNT(DISTINCT CASE WHEN c.age_group = 'Child' THEN c.id END) AS t_child,
COUNT(DISTINCT CASE WHEN c.age_group = 'Infant' THEN c.id END) AS t_infant
-- , COUNT(*) as total
-- , COUNT(c.id) as total_clientid -- count on value doesn't count NULL's
-- , COUNT(DISTINCT c.id) as total_unique_clientid
FROM voucher v
JOIN voucher_client vc ON vc.voucher_id = v.id
JOIN client c ON c.id = vc.client_id
-- LEFT JOIN voucher_hotel vh ON vh.voucher_id = v.id
WHERE v.isDeleted = 0
-- AND c.age_group = 'Adult' -- uncomment this to only count the adults
GROUP BY v.id
ORDER BY v.id
I have a mysql table for cases. It has product name for each product there can be multiple records with different statuses.
MySQL Fiddler : SQL Fiddler Link
SELECT
product , count(*) as totalopen
FROM cases
where status='OPEN'
group by product
union all
SELECT
product , count(*) as totalclosed
FROM cases
where status='CLOSED'
group by product
It is giving me result as
But I want result as
What exactly I am missing in query. Any help will be appreciated.
Try the following code via using Inner Join:
Select a.product, totalopen , totalclosed
from (
SELECT
product , count(*) as totalopen
FROM cases
where status='OPEN'
group by product ) a
inner join (
SELECT
product ,count(*) as totalclosed
FROM cases
where status='CLOSED'
group by product ) b
on a.product = b.product.
Updated:-
For the products that has only one record, so its status has only CLOSED or OPEN use Full Outer Join instead of inner join as next:-
Select isnull(a.product,b.product) product, isnull(totalopen,0) totalopen , isnull(totalclosed,0) totalclosed
from (
SELECT
product , count(*) as totalopen
FROM cases
where status='OPEN'
group by product ) a
full outer join (
SELECT
product ,count(*) as totalclosed
FROM cases
where status='CLOSED'
group by product ) b
on a.product = b.product
Use case expressions to conditional aggregation:
SELECT product,
count(case when status='OPEN' then 1 end) as totalopen,
count(case when status='CLOSED' then 1 end) as totalclosed
FROM cases
where status in ('OPEN', 'CLOSED')
group by product
If you want to include products with only other status (e.g. pending), remove the WHERE clause.
Use case expression and count the relevant ones.
SELECT
product ,
count(case when status = 'OPEN' then 1 end) as totalopen,
count(case when status = 'CLOSED' then 1 end) as totalclosed
FROM cases
group by product;
It uses the fact that
COUNT(1) = 1
COUNT(NULL) = 0
and
case when 1 = 1 then 1 end -- return 1
case when 1 = 2 then 1 end -- return null
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