MYSQL Query users orders if they used a certain coupon code - mysql

I'm trying to find out if a user who used a discount code has ordered after the initial order.
The three tables are: order, user, coupon_uses
From coupon_uses, the only data I'm retrieving is users who used code via:
SELECT *
FROM coupon_uses
WHERE coupon_id = 21921
I would get a return a table with the user ID, order ID, and Coupon they used.
ID Coupon_ID User_ID Order_ID
11 21921 148871 1448181
21 21921 888381 1448191
31 21921 888411 1448201
41 21921 354311 1448211
51 21921 452671 1448221
61 21921 684791 1448231
Now, I need to check the users that are returned in the first query (who used the code) verses the entire order table:
I tried something like this:
SELECT order.user_id,
COUNT(*) AS Total_Orders
FROM `order`
WHERE `order`.user_id = (
SELECT user_id
FROM coupon_uses
WHERE coupon_id = 21921)
AND order.order_status != "Cancelled"
GROUP BY order.user_id ASC
ORDER BY `order`.orderplaced_ts
But I receive Subquery returns more than 1 row.
The desired result would be return a list of user IDs, with the total orders they placed and the date of of the order.
User_ID Total_Orders Last_Order
148871 17 2015_01_01
888381 19 2015_01_01
888411 3 2015_01_14
354311 5 2015_05_01
452671 99 2015_02_01
684791 213 2015_01_05
Thanks.

Try this:
SELECT o.user_id, COUNT(*) AS total_orders
FROM order AS o
INNER JOIN coupon_uses AS c ON o.user_id = c.user_id
WHERE c.coupon_id = 21921
GROUP BY o.user_id
ORDER BY MIN(o.orderplaced_ts)
This assumes that a user can only use a coupon once.

Related

How to find best selling products?

I want to find the best selling products according to this criteria.
Minimum order = 5
Product A = 100 orders -last order = 29 Dec 2021
Product B = 6 orders - last order = 1 Jan 2022
Product C = 3 Orders - last order = 3 Jan 2022
Product B must show first.
Product C will not show because it has less than 5 orders.
Here is my database structure and what I tried
http://sqlfiddle.com/#!9/04e2a92/23
Here's the query for getting all products with minimum order count of 5, sorted in descending order.
SELECT p_name,
tmp.total_orders,
tmp.last_purchased
FROM products P
INNER JOIN (
SELECT product_id,
COUNT(*) AS total_orders,
MAX(created_at) AS last_purchased
FROM order_items
GROUP BY product_id
HAVING total_orders >= 5
) AS tmp ON tmp.product_id = P.id
ORDER BY last_purchased DESC

SQL - min(date) with conditionals

We ran a promotion where users can receive their first subscription order free. Price = $0.00 when a user uses the promo. I am interested in the data from Example A.
Example A - User 50 started with the promo and continued for two months
order_id user_id price created_at
1 50 0.00 2018-01-15
5 50 20.00 2018-02-15
9 50 20.00 2018-03-15
Example B - User 100 was already an active subscriber who cancelled his account and reactivated with the promo, I do not wish to count him
order_id user_id price created_at
2 100 20.00 2018-01-16
3 100 0.00 2018-01-17
7 100 20.00 2018-02-17
--Here is my query--
This returns all users who have multiple orders
WHERE at least one of their orders has a price = 0.00
-This dataset returns example A and example B
--My question--
Most of this data is correct (Example A) but a handful of them I want to omit because they are skewing my data (Example B). I want to remove Example B users.
I want to remove people who's first order was not the promo.
How can I request that their FIRST order had a price = 0.00? I was thinking something with min(created_at)?
You can get the time of the first order using:
select user_id, min(created_at) as min_ca
from t
group by user_id;
Next, you can get the price of the first order using:
select oi.*
from order_items oi join
(select user_id, min(created_at) as min_ca
from order_items oi
group by user_id
) ooi
on oi.user_id = ooi.user_id and oi.created_at = ooi.min_ca
where oi.price = 0.00;
Then you can get all records, using join, in, or exists;
select oi.*
from order_items oi join
order_items oi1
on oi.user_id = oi1.user_id join
(select user_id, min(created_at) as min_ca
from order_items oi
group by user_id
) u1
on oi1.user_id = u1.user_id and oi1.created_at = u1.min_ca
where oi1.price = 0.00;
You can use EXISTS to check that for the record with zero price there is no earlier created_at:
SELECT COUNT(*), user_id
FROM Promo
WHERE user_id IN (
-- Query below yields [user_id]s of users who got the promo
-- that wasn't a result of a cancellation and re-activation
SELECT user_id
FROM Promo p
WHERE p.price = 0 AND NOT EXISTS (
-- Look for a record with the same user ID and an earlier date
-- than p.created_at, which is the date of the promo with 0.00 price
SELECT *
FROM Promo pp
WHERE pp.user_id=p.user_id AND pp.created_at < p.created_at
)
)
GROUP BY user_id

mysql CONCAT after WHERE?

I have a query that manages attendance. I wanted to add a feature that shows who's paid up for the month and who isn't. It almost works.
SELECT
students.sid,
students.name,
students.day,
students.times,
students.days,
CONCAT(payments.year,'-', payments.forMonth) AS pdate
FROM students
LEFT JOIN payments
ON students.sid = payments.sid
WHERE
students.Active = 'Yes' AND
students.day LIKE 'Tue%' AND
payments.date = (SELECT date FROM payments WHERE students.sid = payments.sid AND
payments.payfor = 'tuition' ORDER BY payments.pid DESC LIMIT 1 )
The main trouble part is the (SELECT date FROM)
subquery. The CONCAT is grabbing the first value it gets from the payments table before the payments.payfor limiter in the payments subquery sets. So, in the table snippet below, I get the sk1 entry, instead of the correct tuition for Dec
pid sid amt payfor forMonth year date
1076 69 7000 tuition Dec 2017 2017-12-17
1074 69 4000 sk1 Sep 2017 2017-12-17
1046 69 7000 tuition Nov 2017 2017-11-23
Is there a way to get that payments.date subquery before or with the CONCAT(payments.year,'-', payments.forMonth)?
You should move the condition related to left join tables in the related on clause otherwise if in where condition work as inner join and don't return he related rows
SELECT students.sid, students.name, students.day, students.times, students.days,
CONCAT(payments.year,'-', payments.forMonth) AS pdate
FROM students
LEFT JOIN payments ON students.sid = payments.sid
AND payments.date = (SELECT date
FROM payments
WHERE students.sid = payments.sid
AND payments.payfor = 'tuition'
ORDER BY payments.pid DESC LIMIT 1
)
WHERE students.Active = 'Yes'
AND students.day LIKE 'Tue%'
and if you want only the tuition you should filter for this value too
SELECT students.sid, students.name, students.day, students.times, students.days,
CONCAT(payments.year,'-', payments.forMonth) AS pdate
FROM students
LEFT JOIN payments ON students.sid = payments.sid
AND payments.date = (SELECT date
FROM payments
WHERE students.sid = payments.sid
AND payments.payfor = 'tuition'
ORDER BY payments.pid DESC LIMIT 1
)
AND payments.payfor='tuition'
WHERE students.Active = 'Yes'
AND students.day LIKE 'Tue%'
OK, I got it. I added
AND payments.payfor = 'tuition'
after the
students.Active = 'Yes' AND
students.day LIKE 'Tue%'
in my original query. Then, everything worked as needed.

MySQL: Find the average value per entry for the last x records

I'm trying to figure out how to grab the average rating for each salesperson over their last 100 ratings if they are currently employed, and if they have an average rating less than 3 (out of 5).
I have the following tables (leaving out information that isn't needed in the query):
users
id name employed
-----------------------
1 John 1
2 Sue 1
3 Bob 0
...
sales
id users_id
------------------
100 3
101 2
102 3
103 1
...
ratings
sales_id rating
-----------------
100 4
101 5
102 5
103 2
...
The current query I have searches everything and returns the average for all orders ever but I want it to only grab the most recent 100 ratings (or less if the salesperson hasn't sold that many items), still excluding anyone that is no longer employed or has a rating for their last 100 orders greater than 3. This is the current query:
SELECT u.name, avg(r.rating) as avg_rating, count(r.rating)
FROM users AS u
JOIN sales AS s ON s.users_id = u.id
JOIN ratings AS r ON r.sales_id = s.id
WHERE u.employed = 1
GROUP BY u.id
HAVING avg_rating <= 3;
Any help would be great! Thanks! :D
You can use my sql variables to keep track of the number of ratings so that you can get only recent 100 ratings , ordering by sales_id so you get recent ratings.
SQL FIDDLE DEMO
SELECT T.name, avg(T.rating) as avg_rating, count(T.rating)
FROM
(
SELECT u.name, r.rating, #num := if (#name = name, #num+1, 1) as rn,
#name:= name as var_name
FROM users AS u
JOIN sales AS s ON s.users_id = u.id
JOIN ratings AS r ON r.sales_id = s.id
AND u.employed = 1
JOIN ( select #name :='' , #num :=1) var
order by sales_id desc
)T
where T.rn <=100
GROUP BY T.name
HAVING avg_rating <= 3

Writing a LIMIT subquery within my SQL query

So I have the following query which fetches active competitions for an organisation, but also aims to fetch the user that is in the lead - for each competition fetched.
The query currently works in that it fetches the competitions, however it currently fetches all the users and I would like to LIMIT 1 on the users fetched, using the SUM(activity_weight) you can see below.
The results come out like this (removed some results to make it easy to see) and in my case, I only want to fetch John and Sally, as they are the leaders of the competitions.
competitionId compName start_date end_date name totalPoints
------------------------------------------------------------
123 First Comp 13-09-09 13-10-09 John 100
123 First Comp 13-09-09 13-10-09 Bob 50
431 Second Comp 13-05-04 13-10-05 Sally 500
431 Second Comp 13-05-04 13-10-05 Jessica 50
I understand that I must use some form of subquery to use the LIMIT, but having a problem nailing the syntax of it.
Any help is much appreciated! THANK YOU
SELECT c.competitionId, c.name, c.start_date, c.end_date, a.userid, u.name,
u.profilePic ,
SUM(activity_weight) AS totalPoints
FROM activity_entries a INNER JOIN users1 u ON u.id = a.userid
INNER JOIN competitions c ON c.competitionId = a.competitionId
WHERE c.organisationId = '$organisation' AND c.start_date < now() AND c.end_date > now()
GROUP BY a.userid, c.competitionId ORDER BY c.id DESC, totalPoints DESC
Try this query
select * from
(select
#rn:=if(#prv=competitionId , #rn+1, 1) as rId,
#prv:=competitionId as competitionId ,
totalPoints,
your_other_columns
from (select * from ...)subquery
join
(select #prv:=0, #rn:=0)tmp
order by
competitionId , totalPoints desc) a
-- only top 2 ordered by points for every competition
where rid<=2
output:
rID competitionId compName start_date end_date name totalPoints
------------------------------------------------------------
1 123 First Comp 13-09-09 13-10-09 John 100
2 123 First Comp 13-09-09 13-10-09 Bob 50
1 431 Second Comp 13-05-04 13-10-05 Sally 500
2 431 Second Comp 13-05-04 13-10-05 Jessica 50
change the last part to where rid<=1 to select top 1