I have users and orders tables with this structure (simplified for question):
USERS
userid
registered(date)
ORDERS
id
date (order placed date)
user_id
I need to get array of users (array of userid) who placed their 25th order during specified period (for example in May 2019), date of 25th order for each user, number of days to place 25th order (difference between registration date for user and date of 25th order placed).
For example if user registered in April 2018, then placed 20 orders in 2018, and then placed 21-30th orders in Jan-May 2019 - this user should be in this array, if he placed 25th (overall for his account) order in May 2019.
How I can do this with MySQL request?
Sample data and structure: http://www.sqlfiddle.com/#!9/998358 (for testing you can get 3rd order as ex., not 25th, to not add a lot of sample data records).
One request is not required - if this can't be done in one request, few is possible and allowed.
You can use a correlated subquery to get the count of orders placed before the current one by a user. If that's 24 the current order is the 25th. Then check if the date is in the desired range.
SELECT o1.user_id,
o1.date,
datediff(o1.date, u1.registered)
FROM orders o1
INNER JOIN users u1
ON u1.userid = o1.user_id
WHERE (SELECT count(*)
FROM orders o2
WHERE o2.user_id = o1.user_id
AND o2.date < o1.date
OR o2.date = o1.date
AND o2.id < o1.id) = 24
AND o1.date >= '2019-01-01'
AND o1.date < '2019-06-01';
The basic inefficient way of doing this would be to get the user_id for every row in ORDERS where the date is in your target range AND the count of rows in ORDERS with the same user_id and a lower date is exactly 24.
This can get very ugly, very quickly, though.
If you're calling this from code you control, can't you do it from the code?
If not, there should be a way to assign to each row an index describing its rank among orders for its specific user_id, and select from this all user_id from rows with an index of 25 and a correct date. This will give you a select from select from select, but it should be much faster. The difficulty here is to control the order of the rows, so here are the selects I envision:
Select all rows, order by user_id asc, date asc, union-ed to nothing from a table made of two vars you'll initialize at 0.
from this, select all while updating a var to know if a row's user_id is the same as the last, and adding a field that will report so (so for each user_id the first line in order will have a specific value like 0 while the other rows for the same user_id will have a 1)
from this, select all plus a field that equals itself plus one in case the first added field is 1, else 0
from this, select the user_id from the rows where the second added field is 25 and the date is in range.
The union thingy is only necessary if you need to do it all in one request (you have to initialize them in a lower select than the one they're used in).
Edit: Well if you need the date too you can just select it along with the user_id, but calculating the number of days in sql will be a pain. Just join the result table to the users table and get both the date of 25th order and their date of registration, you'll surely be able to do the difference in code.
I'll try building an actual request, however if you want to truly understand what you need to make this you gotta read up on mysql variables, unions, and conditional statements.
"Looks too complicated. I am sure that this can be done with current DB structure and 1-2 requests." Well, yeah. Use the COUNT request, it will be easy, and slow as hell.
For the complex answer, see http://www.sqlfiddle.com/#!9/998358/21
Since you can use multiple requests, you can just initialize the vars first.
It isn't actually THAT complicated, you just have to understand how to concretely express what you mean by "an user's 25th command" to a SQL engine.
See http://www.sqlfiddle.com/#!9/998358/24 for the difference in days, turns out there's a method for that.
Edit 5: seems you're going with the COUNT method. I'll pray your DB is small.
Edit 6: For posterity:
The count method will take years on very large databases. Since OP didn't come back, I'm assuming his is small enough to overlook query speed. If that's not your case and let's say it's 10 years from now and the sqlfiddle links are dead; here's the two-queries solution:
SET #PREV_USR:=0;
SELECT user_id, date_ FROM (
SELECT user_id, date_, SAME_USR AS IGNORE_SMUSR,
#RANK_USR:=(CASE SAME_USR WHEN 0 THEN 1 ELSE #RANK_USR+1 END) AS RANK FROM (
SELECT orders.*, CASE WHEN #PREV_USR = user_id THEN 1 ELSE 0 END AS SAME_USR,
#PREV_USR:=user_id AS IGNORE_USR FROM
orders
ORDER BY user_id ASC, date_ ASC, id ASC
) AS DERIVED_1
) AS DERIVED_2
WHERE RANK = 25 AND YEAR(date_) = 2019 AND MONTH(date_) = 4 ;
Just change RANK = ? and the conditions to fit your needs. If you want to fully understand it, start by the innermost SELECT then work your way high; this version fuses the points 1 & 2 of my explanation.
Now sometimes you will have to use an API or something and it wont let you keep variable values in memory unless you commit it or some other restriction, and you'll need to do it in one query. To do that, you put the initialization one step lower and make it so it does not affect the higher statements. IMO the best way to do this is in a UNION with a fake table where the only row is excluded. You'll avoid the hassle of a JOIN and it's just better overall.
SELECT user_id, date_ FROM (
SELECT user_id, date_, SAME_USR AS IGNORE_SMUSR,
#RANK_USR:=(CASE SAME_USR WHEN 0 THEN 1 ELSE #RANK_USR+1 END) AS RANK FROM (
SELECT DERIVED_4.*, CASE WHEN #PREV_USR = user_id THEN 1 ELSE 0 END AS SAME_USR,
#PREV_USR:=user_id AS IGNORE_USR FROM
(SELECT * FROM orders
UNION
SELECT * FROM (
SELECT (#PREV_USR:=0) AS INIT_PREV_USR, 0 AS COL_2, 0 AS COL_3
) AS DERIVED_3
WHERE INIT_PREV_USR <> 0
) AS DERIVED_4
ORDER BY user_id ASC, date_ ASC, id ASC
) AS DERIVED_1
) AS DERIVED_2
WHERE RANK = 25 AND YEAR(date_) = 2019 AND MONTH(date_) = 4 ;
With that method, the thing to watch for is the amount and the type of columns in your basic table. Here orders' first field is an int, so I put INIT_PREV_USR in first then there are two more fields so I just add two zeroes with names and call it a day. Most types work, since the union doesn't actually do anything, but I wouldn't try this when your first field is a blob (worst comes to worst you can use a JOIN).
You'll note this is derived from a method of pagination in mysql. If you want to apply this to other engines, just check out their best pagination calls and you should be able to work thinks out.
I have a stock table and a stock history table, and I am basically trying to write a MySQL statement which will get the value of the stock on a particular day (in this case on the 31st of March), which can only be found by multiplying the cost per unit against what the balance for each item was on the particular day
So far I have :
SELECT
SUM(tbl_stock.cost_per_unit * tbl_stock_history.quantity_balance) as total
FROM
tbl_stock
LEFT JOIN
tbl_stock_history ON tbl_stock.part_ID = tbl_stock_history.part_ID
WHERE
tbl_stock_history.date_of_entry <= '20180331'
and tbl_stock.department = 1
AND tbl_stock.qty > 0
Unfortunately, this code takes the sum of ALL qty_balances found against the part ID's history instead of just the most recent one against the booking_date parameter.
I have tried all the solutions I could find with sub select queries but none of them were playing ball and I feel like I am missing something super obvious!
Any help is greatly appreciated!
I think that this is what you are looking for:
SELECT
SUM(tbl_stock.cost_per_unit * t.quantity_balance) as total
FROM
tbl_stock
LEFT JOIN
(
SELECT * FROM tbl_stock_history
WHERE date_of_entry <= '20180331' ORDER BY date_of_entry DESC limit 1
)
t on tbl_stock.part_ID = t.part_ID
WHERE tbl_stock.department = 1
AND tbl_stock.qty > 0
I have a temp table and I'm trying to sum data but can't seem to get the logic right for it. The table contains customer level data and now I'm trying to aggregate it by fiscal year, quarter, and product description. I'm trying to sum by going back 1 year and using the same quarter to sum the # of units sold.
I can do this in excel, but the table is too large for that. This is what the formula in Excel looks like:
=SUMIFS(Units,FiscalYearQuarter >= Concat(FiscalYear -1 & FiscalQuarter, FiscalYearQuarter <= Concat(FiscalYear, FiscalQuarter)
Here's an example of the table:
Here's what the results should looks like (This does not include productdescription, but I will want to add that in):
Every time I try to group by or do a Sum(Case When...) I keep getting the results only by the fiscal year/quarter instead of the sum of historical for 1 year.
A simple GROUP BY will work (although I don't quite understand your Excel logic with concatenation):
SELECT t1.FiscalYear, t1.FiscalQuater, sum(t2.UnitsPurchased)
FROM `table` t1
LEFT JOIN `table` t2
ON ( t1.FiscalYear = t2.FiscalYear + 1
AND t1.FiscalQuater < t2.FiscalQuater)
OR ( t1.FiscalYear = t2.FiscalYear
AND t1.FiscalQuater >= t2.FiscalQuater)
GROUP BY t1.FiscalYear, t1.FiscalQuater
EDIT 1
modified query based on author's feedback
I need to create a small MySQL trigger which will calculate price tax. I have looked and searched for answer and tried lot of advices with IN or INNER JOIN etc.. but I still got this error and I don't know how to solve it. In table products there are lot of product's prices and in table tax there is only one row with tax value. I need each product price divided with this tax value. Thanks a lot for help.
Select ((Select product_price_tax
From products
Where product_price_tax In (Select product_price_tax
From products)
) / (Select tax_value
From tax
))
If am not wrong you are looking for this
SELECT product_price_tax / IFNULL((SELECT tax_value
FROM tax), 0)
FROM products
IFNULL is used to handle divided by zero error in case the tax value is 0
I was cruising along setting up a production schedule at work, but I have been stuck on something that seems like it should be so easy.
I have a table called orders with columns for date, item and quantity.
I am trying to total the quantity of each item for a specific date.
I've been stuck for hours trying all sorts of things. Not sure if I am even close.
For example:
SELECT * FROM orders WHERE date_prod='2011-10-01' AS 'today';
SELECT item, date_prod, SUM( quantity )
FROM today
GROUP BY item
HAVING date_prod = '2011-10-01'
LIMIT 0 , 30
Tried playing around a bunch already. VIEW is not a practical way for me to do this because I want to be able to query a specific date far into the future and see what the total quantity is for each item ordered that day.
Something tells me this should be easy but I'm pretty new at this.
Thanks in advance!
What about:
SELECT item, SUM( quantity ) AS total
FROM orders
WHERE date_prod = '2011-10-01'
GROUP BY item