SQL multiple where and fields - mysql

I currently have data organized in 2 tables as such:
Meetings
meet_id
meet_category
Orders
order_id
meet_id
order_date
I need to write a single query that returns the total number of meetings, the number of meetings with a category of "long" and the number of meetings with a category of "short".
Count only the meetings that have at least one order_date after March 1, 2011.
The output should be in 3 fields and 1 row
So far I what I have is:
SELECT COUNT(m.meet_id),
COUNT(SELECT m.meet_id WHERE m.meet_category = 'long'),
COUNT(SELECT m.meet_id WHERE m.meet_category = 'short')
FROM Meetings m
INNER JOIN Orders o
ON m.meet_id = o.meet_id
WHERE o.order_date >= '2011-03-01';
That is what first comes to mind, but this query doesn't work and I am not even sure if my approach is the correct one. All help appreciated!

Try this:
SELECT COUNT(m.meet_id),
SUM(CASE WHEN m.meet_category = 'long' THEN 1 ELSE 0 END),
SUM(CASE WHEN m.meet_category = 'short' THEN 1 ELSE 0 END)
FROM Meetings m where meet_id in
(select meet_id
FROM Orders o
WHERE o.order_date >= '2011-03-01');

try this
select count(1) as total_count,innerqry.*
(
select count(m.meet_id), meet_catagory
Meetings m
INNER JOIN Orders o
ON m.meet_id = o.meet_id
and m.meet_id in
(select meet_id from orders where order_date>= '2011-03-01')
group by meet_category
) innerqry
if you want only one row and you have few known meeting types try this
SELECT COUNT(m.meet_id),
SUM(CASE WHEN m.meet_category = 'long' THEN 1 ELSE 0 END),
SUM(CASE WHEN m.meet_category = 'short' THEN 1 ELSE 0 END)
FROM Meetings m
INNER JOIN Orders o
ON m.meet_id = o.meet_id
WHERE m.meet_id in
(select meet_id from orders where order_date>'2011-03-01')

Try this:
SELECT COUNT(m.meet_id),
(select count(*) from meetings m2 join orders o2 on o2.meet_id = m2.meet_id where m2.meet_category = 'long' and o2.order_date >= '2011-03-01'),
(select count(*) from meetings m2 join orders o2 on o2.meet_id = m2.meet_id where m2.meet_category = 'short' and o2.order_date >= '2011-03-01')
FROM mettings m
INNER JOIN Orders o
ON m.meet_id = o.meet_id
WHERE o.order_date >= '2011-03-01';
You must implement two indipendent subqueries because you want a total number in your main query without grouped by meet_id, so if you try to link subquery with field in main query you have an error about grouped by fields.

Related

LEFT JOIN with multiple nested select

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;

MySQL subquery in select

I have the following scenario. i have three tables (users, sales, sales_details) Users to Sales is a 1 to 1 relationship and sales to sales_details is 1 to many.
I am running a query where I get all the sales for each user by joining all 3 tables without any issue.
Query looks something like this
SELECT s.month as month,u.name as name, s.year as year, s.date as date,sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,sum(sd.stock) as stock,s.currency as currency,s.user as user
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
group by s.Id
What I want to do now is add an extra field in my query which will be a subquery.
SELECT SUM(total) AS total_yearly
FROM (
SELECT sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month))))
and User = **ID OF USER** ) as sub
This query on its own gives me the sales for the user for the past 12 months while the original query does it per month. I know that the result will be the same for each user but i need it for other calculations.
My problem is how I will join the 2 queries so that the subquery will read the user id from the original one.
Thanks in advance!
Group the second query by user, and then join it with the original query.
SELECT s.month as month,u.name as name, s.year as year, s.date as date,
sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,
sum(sd.stock) as stock,s.currency as currency,s.user as user,
us.total
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
inner join (
SELECT User, sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month)))))
GROUP BY User) AS us ON s.user = us.user
group by s.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.

SQL Query count how many products bought on one day by those who bought more than 3 products on a different day

I want to write a query to count how many chairs were bought on 2015-01-01 by those who bought 2 or more of any products on 2016-01-01
Tables:
Sales (date, customer_id, product_id, units_sold)
Products (id, name, price)
Customers (id, name)
My SQL code so far:
SELECT sum(s.units_sold)
FROM Sales s, Products p
WHERE p.name = 'Chair' and s.date = '2015-01-01' and s.product_id = p.id;
Fiddle: http://sqlfiddle.com/#!9/a0b56
Not very efficient, but produces the desired results
SELECT sum(s.units_sold)
FROM Sales s, Products p
WHERE p.name = 'Chair' and s.date = '2015-01-01' and s.product_id = p.id
AND s.customer_id IN (SELECT customer_id FROM Sales WHERE date = '2016-01-01' AND units_sold > 1)
One method is to use two levels of aggregation, the first at the customer level and the second overall:
SELECT SUM(chairs_2015)
FROM (SELECT s.customer_id,
SUM(CASE WHEN p.name = 'Chair' and s.date = '2015-01-01
THEN s.units_sold
END) as chairs_2015,
FROM Sales s JOIN
Products p
ON s.product_id = p.id
WHERE SUM(CASE WHEN s.date = '2016-01-01 THEN 1 ELSE 0 END) >= 2
GROUP BY s.customer_id
) s;

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