Here is our goal:
We want to select each month and the revenue gained for each month, for a particular user.
Here are our tables
product, purchase, user, months
user 1..* product, product 1..* purchase
product has a column 'user_id' and purchase has a column 'product_id'
months is just a table that contains each month as a string. Currently, we are using this to do some left joins as you can see below.
SELECT months.name, IFNULL(sum(purchase.price), 0) as revenue
FROM months
LEFT JOIN purchase
ON DATE_FORMAT(purchase.purchase_date, '%M') = months.name
AND DATE_FORMAT(purchase_date, '%Y') = DATE_FORMAT(CURRENT_DATE, '%Y')
AND purchase.status = 2
GROUP BY name
ORDER BY months.id ASC;
Which works great for ALL of the users and ALL of the purchases made this month (Purchase status of 2 means complete). The next part is how do I filter this based on a user id? The table 'product' has the user_id we're looking for, and table purchase has a 'product_id'. Everytime I try something it either does nothing or it removes all of the null values, which we don't want.
ATTEMPT #NathanialWools
The where clause will remove all of the rows with a revenue of zero (or null, because of the IFNULL statement). This is what I tried:
SELECT name, IFNULL(sum(purchase.price), 0) as revenue
FROM months
LEFT JOIN purchase
ON DATE_FORMAT(purchase.purchase_date, '%M') = months.name
AND DATE_FORMAT(purchase_date, '%Y') = DATE_FORMAT(CURRENT_DATE, '%Y')
AND purchase.status = 2
LEFT JOIN product
ON product.id = purchase.product_id
WHERE product.user_id = 1
GROUP BY name
ORDER BY months.id ASC;
If you have a list of users you could start with that, join to months, then join to product. (if you don't just start with product).
SELECT u.user_id, m.name, IFNULL(sum(p.price), 0) as revenue
FROM user u
CROSS JOIN months m
LEFT JOIN product d
ON u.user_id = d.user_id
LEFT JOIN purchase p
ON DATE_FORMAT(p.purchase_date, '%M') = m.name
AND DATE_FORMAT(p.purchase_date, '%Y') = DATE_FORMAT(CURRENT_DATE, '%Y')
AND p.product_id = d.product_id
AND p.status = 2
WHERE u.user_id = <user you care about>
GROUP BY u.user_id, m.name
ORDER BY m.id ASC;
LEFT JOIN on product, and add a WHERE clause where user_id is null or user_id is equal to your value.
What did you try?
SELECT months.name, u.user_id, sum(IFNULL(purchase.price, 0)) as revenue
FROM months
LEFT JOIN purchase pu
ON DATE_FORMAT(purchase.purchase_date, '%M') = months.name
AND DATE_FORMAT(purchase_date, '%Y') = DATE_FORMAT(CURRENT_DATE, '%Y')
AND purchase.status = 2
LEFT JOIN product pr
ON pr.product_id = pr.product_id
LEFT JOIN user u
ON u.user_id = pr.user_id
GROUP BY pu.name, u.user_id
ORDER BY months.id ASC;
An inner-select Seems to do the trick. The left join on the purchase alone was not enough filtering. Here is the solution that works for me:
SELECT m.name, IFNULL(sum(b.price), 0) as revenue
FROM months as m
LEFT JOIN (SELECT price, purchase_date, status
FROM purchase, product
WHERE purchase.product_id = product.id
AND product.user_id = 1) as b
ON DATE_FORMAT(b.purchase_date, '%M') = m.name
AND DATE_FORMAT(b.purchase_date, '%Y') = DATE_FORMAT(CURRENT_TIMESTAMP, '%Y')
AND status = 2
GROUP BY m.name
ORDER BY m.id ASC
Where you would obviously change the '1' based on which user you wanted to look at.
Thanks everyone for pitching in, made debugging faster.
Related
im trying to return a list of categories sold by month.
My query:
select count(*) counter, monthname(sales.date) month_name, sales.date, models.name, models.code, models.price, models.size, models.quantity, models.image, models.image_type, models.image_name, categories.name, categories.color from sales inner join models on models.code = sales.code inner JOIN categories on models.category_id = categories.id where year(sales.date) = year(curdate()) GROUP by month(sales.date), categories.name
My result:
This work well but as you can see for month "January" category "Sandaletti" is missing.. but i want to return 0 in counter when this appear like counter 0 month_name January name SANDALETTI
I don't know how i can do.. anyone can help please?
Try something like this, starting by selecting all your categories and then applying the aggregated totals for each.
Edited after you tagged as mysql
select x.*, c.name, c.color /* You may need to specify individual columns in order to use isnull() for where there are no category results */
from categories c
left join (
select Count(*) counter, monthname(s.date) month_name, s.date,
m.name, m.code, m.price, m.size, m.quantity, m.image,
m.image_type, m.image_name, m.category_id
from sales s inner join models m on s.code = m.code
where Year(s.date) = Year(curdate())
group by Month(s.date), m.category_id
)x on x.category_id=c.id
i have the below query:
SELECT transaction_date,tanks.name,stations.name,products.name,tank_sales
FROM tanks_product_movement LEFT JOIN stations ON station_id=stations.id
LEFT JOIN products ON product_id=products.id LEFT JOIN tanks ON
tank_id=tanks.id WHERE products.name='AGO'
AND stations.name='CG-Station'
i want to know the difference in sales between two days i.e sales difference between 2019-04-01 and 2019-04-02 ?
enter image description here
Assuming these two dates would occur only once in the respective table, you could use pivoting logic here:
SELECT
MAX(CASE WHEN transaction_date = '2019-04-02' THEN tank_sales END) -
MAX(CASE WHEN transaction_date = '2019-04-01' THEN tank_sales END) AS sales_diff
FROM tanks_product_movement tpm
LEFT JOIN stations s
ON tpm.station_id = s.id
LEFT JOIN products p
ON tpm.products_id = p.id
LEFT JOIN tanks t
ON tank_id = t.id
WHERE
p.name = 'AGO' AND
s.name = 'CG-Station';
To find the difference between today and yesterday's sales use:
SELECT
MAX(CASE WHEN transaction_date = CURDATE() THEN tank_sales END) -
MAX(CASE WHEN transaction_date = SUBDATE(CURDATE(), 1) THEN tank_sales END)
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 try to obtain a list of all customers my company has not had any assignments for in the last year.
SELECT MAX(assignment_date), full_name
FROM assignments
CROSS JOIN customers
WHERE assignments.customer_id = customers.id
AND assignment_date < '2017-01-01' -- Dynamic value from backend
GROUP BY full_name
ORDER BY assignment_date DESC
This does not seem to work as intended however, since it only returns some customers we did have assignments for in that timeframe. How would I go about implementing such a feature?
Try this code:
SELECT MAX(assignment_date), full_name
FROM customers
where id not in (SELECT id FROM customers inner join assigments on customers.id = assignments.customer_id WHERE assignment_date > '2017-01-01' )
This will return all customers in your database and remove all of them who did have assigments in last year. You should get all customers without assigments before '2017-01-01' as a result
left join customers to assignments where assignments.customer_id IS NULL
and assignment_date greater than '2017-01-01' i.e.
SELECT MAX(assignment_date), full_name
FROM assignments
WHERE assignments.customer_id IN
(SELECT customers.id
FROM customers
LEFT JOIN assignments ON assignments.customer_id = customers.id
WHERE assignments.customer_id IS NULL
AND assignment_date > '2017-01-01')
GROUP BY full_name
ORDER BY assignment_date DESC
I would suggest left join, group by and having:
SELECT MAX(assignment_date), full_name
FROM customers c LEFT JOIN
assignments a
ON a.customer_id = c.id
GROUP BY c.full_name
HAVING MAX(a.assignment_date) < '2017-01-01' OR
MAX(a.assignment_date) IS NULL
ORDER BY MAX(assignment_date) DESC;
It seems you want to show all customers with their last assignment date, but you want to restrict that list to customers that had no assignment since 2017-01-01. This means the dates you will be showing will be null for those customers who had never had an assignment and a date before 2017-01-01 for the others.
So outer join the last dates to the customers and only keep the rows where that date is before 2017-01-01 or null:
select c.full_name, a.max_date
from customers c
left join
(
select customer_id, max(assignment_date) as max_date
from assignments
group by customer_id
) a on a.customer_id = c.customer_id
where a.max_date < date '2017-01-01'
or a.max_date is null
order by a.max_date desc;
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