MySQL Query to get the first user that sums X sales - mysql

I'm looking for help with a query.
I have a users table and a sales table. Sales are stored with their date, amount and quantity.
I need to get the first user that reaches an X sum of amount or Y sum of sales.
So I would need to take the sale date in account in order to know which user gets it first.
Can anyone give me a hand with this query or should I use another approach?
I'm currenlty using MySQL 8.0.32.
Here are some sample tables and an example of what is needed.
Users
id
username
1
user1
2
user2
Sales
id
amount
quantity
date
user_id
1
1000
2
2023-01-05
1
2
1500
3
2023-01-05
2
3
500
1
2023-01-07
1
4
1500
3
2023-01-10
1
5
500
1
2023-01-12
1
6
2500
5
2023-01-12
2
7
1000
2
2023-01-15
1
In this case, lets assume that the goal is the first user to sum >=4000. So user2 would be the winner as it reaches to 4000 before user1 even when at the end user1 sums 4500.
I would expect to get the lists of users untill the date that the first user get to the goal ie:
user_id
sum_amount
date
2
4000
2023-01-12
1
3500
2023-01-12
That would be super, but I could handle getting the first user got to the goal.
Thanks.

For MySql 8.0+ you can use SUM() window function to calculate the running total an sort the rows first by the rows that are equal or greater than 4000 and then the date:
SELECT user_id
FROM sales
ORDER BY SUM(amount) OVER(PARTITION BY user_id ORDER BY date) >= 4000 DESC,
date
LIMIT 1;
For previous versions of MySql use a correlated subquery that emulates the functionality of SUM() window function:
SELECT s1.user_id
FROM sales s1
ORDER BY (SELECT SUM(s2.amount)
FROM sales s2
WHERE s2.user_id = s1.user_id AND s2.date <= s1.date
) >= 4000 DESC,
s1.date
LIMIT 1;
See the demo.

Related

SQL query to create a merged table with varied timestamps and varied column mapping

I am trying to write an complex mySQL query in which there are 2 tables action and revenue what I need is:
From auction table take out location, postal code on the basis of user, cat_id, cat and spent and join with revenue table which has revenue column so as that given cat_id, cat and date I can figure out the returns that each 'postal' is generating.
Complexities:
User is unique key here
In auction table has column 'spent' but its populates only when 'event' column has 'show' but it has 'cat' entry. And 'cat_id' starts populating at any event except show. So need to map cat_id from 'cat' for event 'show' to get the spent for that cat_id.
The date has to be setup such that while joining the tables the timestamp should be compared for plus minus 10 mins. Right now in my query I have 24 hrs duration
Aggregating on postal in desc order to postal giving highest returns
**Auction Table**
dt user cat_id cat location postal event spent
2020-11-01 22:12:25 1 0 A US X12 Show 2
2020-11-01 22:12:25 1 0 A US X12 Show 2 (duplicate also in table)
2020-11-01 22:12:25 1 6 A US X12 Mid null
2020-11-01 22:13:20 2 0 B UK L23 Show 2
2020-11-01 22:15:24 2 3 B UK L23 End null
**Revenue table**
dt user cat_id revenue
2020-11-01 22:14:45 1 6 null
2020-11-01 22:13:20 2 3 3
Want to create final table(by aggregating on revenue for each 'postal' area):
location postal spend revenue returns
UK X12 2 0 0
US L23 2 3 3/2=1.5
I have written a query but unable to figure out solution for above mention 3 complexities:
Select s.location, s.postal, s.spend, e.revenue
From revenue e JOIN
auction s
on e.user = s.user
where s.event in ('Mid','End','Show') and
TO_DATE(CAST(UNIX_TIMESTAMP(e.dt, 'y-M-d') AS TIMESTAMP)) = TO_DATE(CAST(UNIX_TIMESTAMP(s.dt, 'y-M-d') AS TIMESTAMP)) and
s.cat_id in ('3') and
s.cat = 'B'
Any suggestion will be helpful
This answers the question for MySQL, which is the original tag on the question as well as mentioned in the question.
If I understand correctly, your issue is "joining" within a time frame. You can do what you want using a correlated subquery. Then the rest is aggregation, which I think is:
select location, postal, max(spend), max(revenue)
from (select a.*,
(select sum(r.revenue)
from revenue r
where r.user = a.user and
r.dte >= s.dt - interval 10 minute and
r.dte <= s.dte + interval 10 minute
) as revenue
from auction a
where s.event in ('Mid', 'End', 'Show') and
s.cat_id in (3) and
s.cat = 'B'
) a
group by location, postal;

LIMIT and Group By? How do I return the lowest 100 earning customers by country? SQL

Hi I’ve a table database1
3 columns : customer_id , income , country
Customer_id
1001
1002
...
Income
5000
6000
7000
Country
SG
HK
VN
...
How do I write a query that returns the lowest 100 earning customers per country?
Is it possible to return:
Customer ID | country code
1003 SG
1004 SG
...
1007 VN
...
So on
Thanks!
On mySQL 8 you can leverage a window function for this:
SELECT * FROM
(
SELECT
country,
customer_id,
row_number() over(partition by country order by income asc) earn_rank
FROM table
)x
WHERE x.earn_rank <= 100
You can conceive that this window function will sort the rows by country then by income, then start counting up from 1. Each time the country changes the row numbering starts over from 1 again. This means that for every country there will be a row numbered 1 (with the lowest income), and a 2, 3 etc. If we then wrap it up in another outer query that selects only rows where the number is less than 101 we get 100 rows per country

MYSQL Cannot count the occurrence of a particular value in a date range

So, there is an account number and we have daily information about their payments. Suppose we have information of 1 year leading up to today which is 08/March/2019, I would want to calculate the number of times he/she overpaid in last 1 week. I have used mysql window function but for some reason it does not seem to work
#GMB A sample data would look like this:Suppose for this account we have info from last march 2018. I just want the number of times paid_status = overpaid from the last date that I have on my file which is of today - 08/March/2019 and previous 7 days, 14 days, 1 month or any duration of my choosing. Your query will hardcode it only for 7 days.
ACCOUNT_ID paid_status amt dte
-----------------------
1234 overpaid 100 01/March/2018
.
.
.
1234 overpaid 120 01/March/2019
1234 not paid 0 02/March/2019
1234 overpaid 110 03/March/2019
1234 overpaid 120 04/March/2019
1234 overpaid 130 05/March/2019
1234 overpaid 120 06/March/2019
1234 overpaid 120 07/March/2019
1234 overpaid 121 08/March/2019
Query:
,COUNT(CASE WHEN paid_status = 'OVERPAID' THEN 1 END)
over (PARTITION BY ACCOUNT_ID
ORDER BY DTE ROWS BETWEEN 7 PRECEDING AND UNBOUNDED FOLLOWING
) AS num_times_overpaid_week1
The output should be like this(not including today's info):
account_id num_times_overpaid_week1
1234 6
While I am getting multiple rows for the same account_id and it is not exactly calulating the field correctly
From your sample data it seems like you are looking for a simple aggregated query (no need for window functions):
SELECT account_id, SUM(paid_status = 'OVERPAID') AS num_times_overpaid_week1
FROM mytable
WHERE dte >= CURRENT_DATE - INTERVAL 7 DAY
GROUP BY account_id
Expression SUM(paid_status = 'OVERPAID') uses a nice MySQL feature where conditions return 1 when satisfied and 0 when not.
NB: if, for some reason, you do want to use window functions (maybe to perform other computation), then you would need to use ROW_NUMBER() to rank records by date, and the filter out only the most recent record per account in an outer query. I think that the definition of the window can be largely simplified:
SELECT *
FROM (
SELECT
account_id,
SUM(paid_status = 'OVERPAID') OVER(PARTITION BY account_id) AS num_times_overpaid_week1,
-- possibly other columns
ROW_NUMBER() OVER(PARTITION BY account_id ORDER BY dte DESC) rn
FROM mytable
WHERE dte >= CURRENT_DATE - INTERVAL 7 DAY
) x WHERE rn = 1

Get top 10 users data based on the number of sales

I have table 'sales' and data like this scheme:
User | Amount| Month
user a 100 1
user b 240 1
user c 120 1
user a 200 2
user b 130 2
user c 240 2
How to get TOP 5 user based on Total Sales every month, I've tried using query like this, but there's always showed an error
SELECT TOP10 USER,
SUM(amount) amount
FROM sales LIMIT 10
WHERE MONTH BETWEEN 1 AND 12
GROUP BY sales
ORDER BY 2 DESC
And the result should be:
User a | 300
User b | 370
User c | 360
So the order must be: B, C, A
You can do a GROUP BY on User, and use SUM(amount) to get the total_sales per user. Now, simply sort the result-set by total_sales in Descending order, to get the highest sales first.
We can use LIMIT 10, in case you want to get only Top 10.
SELECT User,
SUM(amount) AS total_sales
FROM sales
WHERE MONTH BETWEEN 1 AND 12
GROUP BY User
ORDER BY total_sales DESC
LIMIT 10
You can try below
SELECT USER, SUM(amount) amount
FROM sales
WHERE MONTH BETWEEN 1 AND 12
GROUP BY USER
ORDER BY amount DESC

MySQL Query for Average Grade of last 2 attempts

I have a table:
quiz userid attempt grade
1 3 1 33
1 3 2 67
1 3 3 90
1 3 4 20
Now, I want the last two attempts i.e., 4 and 3 and I want average grade of these 2 grades i.e, 90 and 20
Could anyone help me?
Use ORDER and LIMIT to get the 2 last attempts and the AVG aggregation function :
SELECT AVG(grade) AS average FROM (
SELECT grade FROM table
WHERE userid = 3
ORDER BY attempt DESC LIMIT 2) AS t
If you want to list both test results separately, with the average in each row, then something like this maybe (otherwise you just need the subquery for the average of the two tests):
SELECT userid, attempt, grade,
( SELECT AVG(grade)
FROM table
ORDER BY attempt DESC LIMIT 0, 2 ) AS avg_grade
FROM table
ORDER BY attempt DESC LIMIT 0, 2;