mysql query items with the largest price increase - mysql

I have a table 'item_prices' with:
resource_id, avg_price, time_stamp, samples
Items gets a new average price inserted into the table every day. Old averages are not deleted.
How can I query the 10 items with the highest "percent increase in price" since yesterdays average? I would also like to check that the samples is > 10 to ensure accuracy.
to clarify "percent increase in price":
percent_increase = (todays_avg_price - yesterdays_avg_price) / yesterdays_avg_price
example
resource_id | avg_price | time_stamp | samples
1 450 1380526003 12
2 650 1380526002 2
3 980 1380526001 68
1 400 1380440003 24
2 700 1380440002 13
3 400 1380440001 38
1 900 1380300003 11
2 250 1380300002 8
3 300 1380300001 4
returns
resource id | percent_increase
3 1.45
1 0.125

select
today.resource_id,
(today.avg_price - yesterday.avg_price) / yesterday.avg_price as percent_increase
from
item_prices today,
item_prices yesterday
where today.resource_id = yesterday_resource_id
and DATE(FROM_UNIXTIME(today.timestamp)) = $today
and DATE(FROM_UNIXTIME(yesterday.timestamp)) = $yesterday
order by percent_increase desc
limit 10
This is a self join; apologies for the archaic join syntax, it's a bad habit I find hard to shake.

Please try the query below..
select top 1 resource_id,avg_price from item_Prices
group by resource_id,avg_price
having count(resource_id,avg_price)>
10 order by time_stamp

Related

SQL subquery in SELECT clause

I'm trying to find admin activity within the last 30 days.
The accounts table stores the user data (username, password, etc.)
At the end of each day, if a user had logged in, it will create a new entry in the player_history table with their updated data. This is so we can track progress over time.
accounts table:
id
username
admin
1
Michael
4
2
Steve
3
3
Louise
3
4
Joe
0
5
Amy
1
player_history table:
id
user_id
created_at
playtime
0
1
2021-04-03
10
1
2
2021-04-04
10
2
3
2021-04-05
15
3
4
2021-04-10
20
4
5
2021-04-11
20
5
1
2021-05-12
40
6
2
2021-05-13
55
7
3
2021-05-17
65
8
4
2021-05-19
75
9
5
2021-05-23
30
10
1
2021-06-01
60
11
2
2021-06-02
65
12
3
2021-06-02
67
13
4
2021-06-03
90
The following query
SELECT a.`username`, SEC_TO_TIME((MAX(h.`playtime`) - MIN(h.`playtime`))*60) as 'time' FROM `player_history` h, `accounts` a WHERE h.`created_at` > '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
Outputs this table:
Note that this is just admin activity, so Joe is not included in this data.
from 2021-05-06 to present (yy-mm-dd):
username
time
Michael
00:20:00
Steve
00:10:00
Louise
00:02:00
Amy
00:00:00
As you can see this from data, Amy's time is shown as 0 although she has played for 10 minutes in the last month. This is because she only has 1 entry starting from 2021-05-06 so there is no data to compare to. It is 0 because 10-10 = 0.
Another flaw is that it doesn't include all activity in the last month, basically only subtracts the highest value from the lowest.
So I tried fixing this by comparing the highest value after 2021-05-06 to their most previous login before the date. So I modified the query a bit:
SELECT a.`Username`, SEC_TO_TIME((MAX(h.`playtime`) - (SELECT MAX(`playtime`) FROM `player_history` WHERE a.`id` = `user_id` AND `created_at` < '2021-05-06'))*60) as 'Time' FROM `player_history` h, `accounts` a WHERE h.`created_at` >= '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
So now it will output:
username
time
Michael
00:50:00
Steve
00:50:00
Louise
00:52:00
Amy
00:10:00
But I feel like this whole query is quite inefficient. Is there a better way to do this?
I think you want lag():
SELECT a.username,
SEC_TO_TIME(SUM(h.playtime - COALESCE(h.prev_playtime, 0))) as time
FROM accounts a JOIN
(SELECT h.*,
LAG(playtime) OVER (PARTITION BY u.user_id ORDER BY h.created_at) as prev_playtime
FROM player_history h
) h
ON h.user_id = a.id
WHERE h.created_at > '2021-05-06' AND
a.admin > 0
GROUP BY a.username;
In addition to the LAG() logic, note the other changes to the query:
The use of proper, explicit, standard, readable JOIN syntax.
The use of consistent columns for the SELECT and GROUP BY.
The removal of single quotes around the column alias.
The removal of backticks; they just clutter the query, making it harder to write and to read.

How to get cumulative total for previous month and upto this month?

ID pcID contractor approver claimed
-------------------------------------------
1 1 one 1000 900
2 1 two 200 100
3 1 three 1000 1000
4 1 six 100 11
5 2 six 100 22
6 3 six 120 1
7 4 three 102 10
From the above table, I need to get cumulative amount for upto this month and previous month of approver and claimed and also current month approver, claimed amount based on the contractor. Like below table.
ID contractor approver claimed uptothisMTApprover uptothisMTClaimed previousMTApprover previousMTClaimed
-----------------------------------------------------------------------------------------------------------------
1 one 1000 900 1000 900 0 0
2 two 200 100 200 100 0 0
3 three 102 10 1102 1010 1000 1000
4 six 120 1 320 34 200 33
Thanks in advance..
You seem to want the latest row per contractor, as defined by pcID, and a cumulative sum of all previous months.
You can use window functions:
select contractor, approver, claimed,
total_approver as uptothisMTApprover,
total_claimed as uptothisMTClaimed,
total_approver - approver as previousMTApprover,
total_claimed - claimed as previousMTClaimed
from (
select t.*,
row_number() over(partition by contractor order by pcID desc) rn,
sum(approver) over(partition by contractor) total_approver,
sum(claimed) over(partition by contractor) total_claimed
from mytable t
) t
where rn = 1

How can I get the quantity both expired and not yet expired?

I subtract the quantity of expired medicine to the actual stock. I use this (IFNULL(tbl_medicine.quantity - SUM(tbl_received.received_quantity),0)) AS Total and DATE(NOW()) > tbl_received.expiration_date to get the date of expired medicine. My problem is I can't get the quantity that is not expired yet. How can I get the quantity of not yet expired with expired medicine? Can somebody help me with my problem? Here's my query...
SELECT tbl_med.sup_med_id, tbl_med.quantity,
tbl_received.received_quantity, tbl_med.status,
(IFNULL(tbl_med.quantity - SUM(tbl_received.received_quantity),0)) AS Total
FROM
tbl_med
INNER JOIN
tbl_received ON tbl_received.sup_med_id = tbl_med.sup_med_id
WHERE
tbl_med.status = 'Active' AND DATE(NOW()) > tbl_received.expiration_date AND
tbl_med.barangay_id = 19
GROUP BY
sup_med_id HAVING Total > 0
ORDER BY
sup_med_id
output of query above
sup_med_id quantity received_quantity status Total
3 1800 1000 Active 800
7 1800 1000 Active 800
tbl_med & tbl_received table
id sup_med_id received_quantity expiration_date
1 3 1000 2019-09-04
2 7 1000 2019-09-04
3 9 1800 2022-09-04
medicine_id sup_med_id quantity status barangay_id
1 3 1800 Active 19
2 7 1800 Active 19
3 9 1800 Active 19
I want this to happen...
sup_med_id quantity received_quantity status Total
3 1800 1000 Active 800
7 1800 1000 Active 800
9 1800 1800 Active 1800
I just want to display the quantity to Total if the medicine is not expired yet
The issue comes from the fact you're only joining when the expiration date is in the past. To solve it that check should be removed from the WHERE clause and done in the SUM of expired quantities instead:
SELECT tbl_med.sup_med_id, tbl_med.quantity,
tbl_received.received_quantity, tbl_med.status,
(tbl_med.quantity
- SUM(CASE WHEN NOW() > tbl_received.expiration_date
THEN tbl_received.received_quantity
ELSE 0
END)
) AS Total
FROM
tbl_med
INNER JOIN
tbl_received ON tbl_received.sup_med_id = tbl_med.sup_med_id
WHERE
tbl_med.status = 'Active' AND
tbl_med.barangay_id = 19
GROUP BY
sup_med_id HAVING Total > 0
ORDER BY
sup_med_id
I haven't tested the query myself, but I believe it should achieve what you want

mysql group by day and count then filter only the highest value for each day

I'm stuck on this query. I need to do a group by date, card_id and only show the highest hits. I have this data:
date card_name card_id hits
29/02/2016 Paul Stanley 1345 12
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Dave Mustaine 1349 10
25/02/2016 Ozzy 1351 17
23/02/2016 Jhonny Cash 1353 13
23/02/2016 Elvis 1355 15
20/02/2016 James Hethfield 1357 9
20/02/2016 Max Cavalera 1359 12
My query at the moment
SELECT DATE(card.create_date) `day`, `name`,card_model_id, count(1) hits
FROM card
Join card_model ON card.card_model_id = card_model.id
WHERE DATE(card.create_date) >= DATE(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND card_model.preview = 0
GROUP BY `day`, card_model_id
;
I want to group by date, card_id and filter the higher hits result showing only one row per date. As if I run a max(hits) with group by but I won't work
Like:
date card_name card_id hits
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Ozzy 1351 17
23/02/2016 Elvis 1355 15
20/02/2016 Max Cavalera 1359 12
Any light on that will be appreciated. Thanks for reading.
Here is one way to do this. Based on your sample data (not the query):
select s.*
from sample s
where s.hits = (select max(s2.hits)
from sample s2
where date(s2.date) = date(s.date)
);
Your attempted query seems to have no relationship to the sample data, so it is unclear how to incorporate those tables (the attempted query has different columns and two tables).

MySQL sum amounts for transactions related to loans, in reverse order

I'm trying to add an amount living in a transactions table, but the adding must be done taking the transactions in reversed order, grouped by a loan they belong to. Let me put some examples:
Transactions
tid loanid amount entrydate
------------------------------------
1 1 1,500 2013-06-01
2 2 1,500 2013-06-01
3 1 1,000 2013-06-02
4 3 2,300 2013-06-04
5 5 2,000 2013-06-04
6 1 1,100 2013-06-07
7 2 1,000 2013-06-09
| | | |
Loans
loanid
------
1
2
3
4
5
|
As you can see, there's no transactions for loanid 4, just to make clear the point that there's no obligation for the transactions to exist for every loan.
Now, what I'm trying to achieve is to sum up the amounts for the transactions of each loan. This first approach achieves this:
SELECT tr.tid,
l.loanid,
tr.entrydate,
tr.amount,
#prevLoan:=l.loanid prevloan,
#amnt:=if(#prevLoan:=l.loanid, #amnt+tr.amount, tr.amount) totAmnt
FROM (SELECT * FROM Transactions) tr
JOIN (SELECT #prevLoan:=0, #amnt:=0) t
JOIN Loans l
ON l.loanid = tr.loanid
GROUP BY l.loanid, tr.tid
Which achieves something like this:
tid loanid entrydate amount prevloan totAmnt
-----------------------------------------------------------
1 1 2013-06-01 1,500 1 1,500
3 1 2013-06-02 1,000 1 2,500
6 1 2013-06-07 1,100 1 3,600 <-- final result for loanid=1
2 2 2013-06-01 1,500 2 1,500
7 2 2013-06-09 1,000 2 2,500 <-- final result for loanid=2
4 3 2013-06-04 2,300 3 2,300 <-- final result for loanid=3
5 5 2013-06-04 2,000 5 2,000 <-- final result for loanid=5
| | | | | |
As you can see, for each loan, the amount of the transactions belonging to it are getting sumed up in the totAmnt column, so the last transaction of each loan has the total sum of the transactions for the same loan.
Now.... what I actually need is something to get the sum to be done in reversed order. I mean, for the same transactions of each loan, the sum still gets the same result, but I need the sum to be done from the last transaction of each loan up to the first one.
I've tried the following, but to no avail (it's the same query as the last one, but with an Order By DESC on the FROM transactions table):
SELECT tr.tid,
l.loanid,
tr.entrydate,
tr.amount,
#prevLoan:=l.loanid prevloan,
#amnt:=if(#prevLoan:=l.loanid, #amnt+tr.amount, tr.amount) totAmnt
FROM (SELECT * FROM Transactions ORDER BY tr.entrydate DESC) tr
JOIN (SELECT #prevLoan:=0, #amnt:=0) t
JOIN Loans l
ON l.loanid = tr.loanid
GROUP BY l.loanid, tr.tid
I'm using tr.entrydate because is a more familiar way to say the order criteria, and besides that's what policy says is the valid order criteria, tid may say something but entrydate is the ordering column of the Transactions table...
Using the previous query, I just get the same results I get with the first query, so I guess something must be missing there. What I need is to get results as the following:
tid loanid entrydate amount prevloan totAmnt
-----------------------------------------------------------
6 1 2013-06-07 1,100 1 1,100
3 1 2013-06-02 1,000 1 2,100
1 1 2013-06-01 1,500 1 3,600 <-- final result for loanid=1
7 2 2013-06-09 1,000 2 1,000
2 2 2013-06-01 1,500 2 2,500 <-- final result for loanid=2
4 3 2013-06-04 2,300 3 2,300 <-- final result for loanid=3
5 5 2013-06-04 2,000 5 2,000 <-- final result for loanid=5
| | | | | |
As you can see the sum for each loanid gets the same final result, but the sum is done for the transactions in reversed order...
Hope all this mess is clear... How can I achieve such a result?
You appear to be VERY close... I think you have two small adjustments. First, dont use the outer GROUP BY as you are not doing any aggregations (sum, min, max, avg, etc). Second, when querying your transaction table, just order that by the loan ID FIRST, THEN the date descending... This way all the loan IDs are in proper order grouped together, but within each loan, THEY are sorted descending order as you are looking for. Also, adjust your #prevLoan AFTER you have accumulated so the current record can be compared to the next. You are starting the #variable to zero so it won't match the first loan ID on the first run anyhow. Finally, you don't even need the join to the loan table since the transaction table has the loan ID to use as basis of comparison test. Since the inner-most query is ordered by loan and then entry date descending, you should not need it again at the outer query.
SELECT
tr.tid,
tr.loanid,
tr.entrydate,
tr.amount,
#amnt := if( #prevLoan = tr.loanid, #amnt+tr.amount, tr.amount) totAmnt,
#prevLoan := tr.loanid prevloan
FROM
( SELECT *
FROM Transactions
ORDER BY loanid, entrydate DESC) tr
JOIN (SELECT #prevLoan := 0,
#amnt := 0) t
Alternate Solution?? Per my comment, it looks like you want the high totals and shrinking down... Is this closer?
SELECT
tr.tid,
tr.loanid,
tr.entrydate,
tr.amount,
trTotals.TotalLoans - if( #prevLoan = tr.loanid, #amnt, 0 ) as NewBal,
#amnt := if( #prevLoan = tr.loanid, #amnt+tr.amount, tr.amount) runningTotals,
#prevLoan := tr.loanid prevloan
FROM
( SELECT *
FROM Transactions
ORDER BY loanid, entrydate DESC) tr
JOIN ( SELECT loanid, sum( amount ) as TotalLoans
FROM Transactions
group by loanid ) trTotals
on tr.loanid = trTotals.loanid
JOIN (SELECT #prevLoan := 0,
#amnt := 0) t
Produces... (Total Paid) (for reversing calc)
tid loanid entrydate amount NewBal Running Totals prevLoan
6 1 2013-06-07 1100 3600 1100 1
3 1 2013-06-02 1000 2500 2100 1
1 1 2013-06-01 1500 1500 3600 1
7 2 2013-06-09 1000 2500 1000 2
2 2 2013-06-01 1500 1500 2500 2
4 3 2013-06-04 2300 2300 2300 3
5 5 2013-06-04 2000 2000 2000 5