MySQL subquery in select - mysql

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

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;

How to calculate average value?

I have two tables. Into the first table (activity) there are: user_id, sessions and login_time. Into the second (payments) there's only one column - user_id.
I need to calculate how many sessions did users from "payments" do during 01/01/2018-01/05/2018.
activity
user_id sessions login_time
1 6 01.01.2018
2 2 01.01.2018
1 1 02.01.2018
4 3 02.01.2018
1 2 03.05.2018
3 04.02.2018
table
payments
user_id
1
3
4
My code (calculates wrong):
select login_time, avg(activity.sessions) AS
average_sessions
from activity inner join
payments
on payments.user_id = activity.user_id
where activity.login_time between '2018-01-01' and '2018-05-01'
group by activity.login_time;
Thanks for help!
Output:
output
login_time average_sessions
01.01.2018 6
02.01.2018 2
03.01.2018 0
04.01.2018 0
...
looking to your sample
seems you need the sum of session divided by the number of totals days
select user_id, sum(sessions)/t.num_days
from activity
cross join (
select count(distinct login_time) num_days
from activity
) t
group by user_id
and if you need only the user_id in payments
select user_id, sum(sessions)/t.num_days
from activity
cross join (
select count(distinct login_time) num_days
from activity
) t
inner join payments p on p.user_id = activity.user_id
group by user_id
and looking to your formaed result seems you need
select t1. login_time, ifnull(t2.avg_sess) average_sessions
from (
select distinct login_time
from activity
) t1
left join (
select a.login_time, avg(a.session) avg_sess
from activity a
inner join payments p on a.user_id = p.user_id
group by a.login_time
) t2 on t1.login_time = t2.login_time
Based on your expected output, use the following:
SELECT login_time,
SUM(activity.sessions)/COUNT(DISTINCT activity.user_id) AS average_sessions
FROM activity
INNER JOIN payments ON payments.user_id = activity.user_id
WHERE activity.login_time BETWEEN '2018-01-01' and '2018-05-01'
GROUP BY activity.login_time;

SQL find highest date in all entries with a date older than x

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;

MYSQL - Issue with sub-query

SELECT DISTINCT(id_no), lastname,
(SELECT COUNT(purchasedate) num_of_purch
FROM sales JOIN Artist ON
sales.id = Artist.id_no
WHERE DATE_SUB(CURDATE(),INTERVAL 1
YEAR) <= purchasedate
) AS num_of_purch
FROM Artist
This query returns the all Artist's ID_no, and their last name and the total number of purchases, altho i want to specify which purchases were to which artist. Help in solving this would be greatly apprciated.
EDIT - DISTINCT(id_no) is redundant as it is a primary key.
This shows the number of sales for each artist_id:
SELECT artist.id_no, count(sales.id) as num_of_purch
FROM artist left join sales on sales.id = artist.id_no
WHERE DATE_SUB(CURDATE(), INTERVAL 1 YEAR) <= purchasedate
GROUP BY artist.id
To return also the last names, and all of the details:
SELECT art_tot.id_no, art_tot.lastname, art_tot.num_of_purch, sales.*
FROM (SELECT artist.id_no, artist.lastname, count(sales.id) as num_of_purch
FROM artist left join sales on sales.id = artist.id_no
WHERE DATE_SUB(CURDATE(), INTERVAL 1 YEAR) <= purchasedate
GOUP BY artist.id, artist.lastname) art_tot
left join sales on art_tot.id_no = sales.id
This should give you artist and number of purchases per artist
select a.id_no, a.lastname, count(s.purchasedate) num_of_purch
from artists a
join sales s on a.id_no = s.id
where date_sub(curdate(), interval 1 year) <= s.purchasedate
group by a.id_no, a.lastname
You should use a GROUP BY to get the count per artist.
And you should use an outer join to include artists who have no sales within the last year.
SELECT a.id_no, a.lastname, COUNT(s.purchasedate) AS num_of_purch
FROM Artist a
LEFT OUTER JOIN sales s ON s.id = a.id_no
AND s.purchasedate => CURDATE() - INTERVAL 1 YEAR
GROUP BY a.id_no;
PS: Using DISTINCT(id_no) is meaningless not only because id_no is already a unique key, but because DISTINCT always applies to all columns in the select list, even if you add parentheses to make it look like a function that applies only to one column.

SQL Query Help for Chart Data

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.