mysql CONCAT after WHERE? - mysql

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.

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

need a row even for missing values on mysql query

Maybe my google fu is off.
I have a table: sub_transfer_jobs
job_date mo_id sub_quantity shift_id
2017-05-16 24581 12 1
2017-05-16 86122 8 2
etc.
Another table: mo_numbers
mo_id customer
24581 cust1
86122 cust2
68515 cust1
etc.
I have another table: Calendar.
This just has a date value for every date
What i need to get is a string list of amounts per day per customer
example
2017-05-15 cust1 50
2017-05-15 cust2 0
2017-05-16 cust1 22
2017-05-16 cust2 10
etc
I was going to get a distinct list of customers based one what customers are seen based on a date range
This is where i am but have a problem on the sub query
SELECT `sub_transfer_jobs`.`sub_quantity`, `sub_transfer_jobs`.`job_date`, `sub_transfer_jobs`.`shift_id`, `mo_numbers`.`customer`
FROM `sub_transfer_jobs`
join mo_numbers on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
right join calendar on `calendar`.`datefield` = `sub_transfer_jobs`.`job_date`
right join (
select DISTINCT `mo_numbers`.`customer` from sub_transfer_jobs
join `mo_numbers` on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
where job_date > '2017-04-15'
) as customerList on customerList.customer = mo_numbers.customer
where job_date > '2017-04-15'
group by `mo_numbers`.`customer`, `sub_transfer_jobs`.`job_date`
EDIT: Correct sql to get what I needed (grouped for comma seperated)
select basecustomer, GROUP_CONCAT(IFNULL(customerdaytotals.total, 0) ORDER BY datefield ASC) AS total FROM
(select basecustomer, datefield from
(select distinct `mo_numbers`.`customer` as basecustomer
FROM `sub_transfer_jobs`
JOIN `mo_numbers` on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
where `sub_transfer_jobs`.`job_date` > '2017-04-01') as used_customers
CROSS JOIN (
SELECT `calendar`.`datefield` from `calendar`
WHERE `calendar`.`datefield` > '2017-04-01' AND `calendar`.`datefield` < '2017-05-11'
) as daterange) as basedata
LEFT JOIN (
select `sub_transfer_jobs`.`job_date`, `mo_numbers`.`customer`, sum(`sub_transfer_jobs`.`sub_quantity`) as total
FROM `sub_transfer_jobs`
JOIN `mo_numbers` on `mo_numbers`.`mo_id` = `sub_transfer_jobs`.`mo_id`
GROUP BY `sub_transfer_jobs`.`job_date`, `mo_numbers`.`customer`
) as customerdaytotals on customerdaytotals.job_date = basedata.datefield and customerdaytotals.customer = basedata.basecustomer
GROUP BY basecustomer
Which gives my result of
cust1 0,1857,1262,1166,517,1551,0,0,1469,1670,400,0,0,0,...
cust2 0,123,7,0,7,0,0,0,0,0,0,0,0,0,0,84,70,9,53,3,0,0,4...
cust3 0,0,75,425,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0...
cust4 0,0,41,36,44,26,0,0,0,41,0,0,0,0,0,16,88,12,0,0,0,...
cust5 0,277,552,433,280,491,0,0,124,880,1269,0,0,0,0,495...
cust6 0,255,124,620,184,129,0,0,309,103,88,0,0,0,0,118,2...
cust7 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,...
cust8 0,209,123,15,84,296,0,0,296,15,262,0,0,0,0,301,200,...
You need to use CROSS JOIN to get all combinations of customers and dates. Then LEFT JOIN that with a query that gets each customer's total for each date.
SELECT m.date, c.customer, IFNULL(t.total, 0) AS total
FROM mo_numbers AS m
CROSS JOIN customer AS c
LEFT JOIN (
SELECT date, mo_id, SUM(quantity) AS total
FROM mo_numbers
GROUP BY date, mo_id
) AS t ON m.mo_id = t.mo_id AND c.mo_id = t.mo_id

MYSQL Query users orders if they used a certain coupon code

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.

MySQL nested, nested subquery not getting outter variable

I have a spendings table and a dates table, that are joined by date_id and id...
What I'm trying to do, is get from 1 query all the info from spendings, plus the sum of all the spendings but with a limit and/or offset
This is the query right now
SELECT spendings.id, spendings.price, spendings.title,
dates.date, users.username, currencies.value,
( SELECT SUM(sum_table.price)
FROM (
SELECT s.price
FROM spendings s, dates d
WHERE s.date_id = d.id
AND day(d.date) = 25
LIMIT 2 OFFSET 0
) as sum_table
) AS sum_price
FROM spendings, dates, users, currencies
WHERE spendings.date_id = dates.id
AND day(dates.date) = 25
AND spendings.user_id = users.id
AND spendings.curr_id = currencies.id
LIMIT 2 OFFSET 0
Output
id price title date username value sum_price
3 6.00 title1 2013-11-25 alex € 21.00
4 15.00 title2 2013-11-25 alex € 21.00
It works, but only if the date here day(d.date) = 25 is the same as the outer one here day(dates.date) = 25
If instead I put day(d.date) = day(dates.date) which seems the logic thing to do, I get #1054 - Unknown column 'dates.date' in 'where clause'
If anyone has an idea to make this simpler let me know :)
Try to join instead of using nested correlated subqueries:
SELECT spendings.id, spendings.price, spendings.title,
dates.date, users.username, currencies.value,
y.sum_price
FROM spendings, dates, users, currencies
JOIN (
SELECT day, SUM(sum_table.price) As sum_price
FROM (
SELECT day(d.date) As day,
s.price
FROM spendings s, dates d
WHERE s.date_id = d.id
AND day(d.date) = 25
LIMIT 2 OFFSET 0
) sum_table
GROUP BY day
) y
ON y.day = day(dates.date)
WHERE spendings.date_id = dates.id
-- AND day(dates.date) = 25 <== commented since it's redundant now
AND spendings.user_id = users.id
AND spendings.curr_id = currencies.id
Some remarks:
Using old join syntax with commas is not recommended: FROM table1,table2,table2 WHERE
The recommended way of expressing joins is "new" ANSI SQL join syntax:
FROM table1
[left|right|cross|[full] outer|natural] JOIN table2 {ON|USING} join_condition1
[left|right|cross|[full] outer|natural] JOIN table3 {ON|USING} join_condition2
....
Actually this "new syntax" is quite old now, since is has been published, as I remember, in 1992 - 22 years ago. In IT industry 22 years is like 22 ages.