Using created variable in WHERE (MySQL) - mysql

I have a table of bank transactions in MySQL that appear as so:
User ID
Date Created
Currency
Amount
USD_amt
1
April 1
USD
1000
1000
1
May 2
GBP
100
141.90
2
April 2
USD
50
50
2
May 5
EUR
200
243.85
The USD_amt is a calculated field from two other tables. I'd like to get the average USD Amount by User ID as well as average amount by user ID by month and then filter on the rows where the monthly average is 10 times the user average
Right now, I am trying the following
SELECT
t.user_id,
t.created_date,
month(t.created_date),
year(t.created_date),
(t.AMOUNT * fx.rate / POWER(10, cd.exponent)) USD_amt,
avg(t.AMOUNT * fx.rate / POWER(10, cd.exponent)) monthly_avg,
avg(t.AMOUNT * fx.rate / POWER(10, cd.exponent)) over (partition by t.user_id) user_avg
from
transactions t
join fx_rates fx
on (fx.ccy = t.currency and fx.base_ccy = 'USD')
join currency_details cd
on cd.currency = t.currency
where
monthly_avg > 10* user_avg
group by
t.user_id,
t.created_date,
month(t.created_date),
year(t.created_date)
Although, it doesn't appear I can use the created variable in the WHERE function.
Any ideas?

You can not use results for the query in the WHERE clause, because WHERE is executed before the results are calculated.
If you want to apply a filter based on the results, you need to use HAVING, which is executed after.
Of course this has performances implications: WHERE allows you to retrieve a subset of results, while HAVING returns all the rows then filters them.
Here's a quick fix on your code that should work
SELECT
t.user_id,
t.created_date,
month(t.created_date),
year(t.created_date),
(t.AMOUNT * fx.rate / POWER(10, cd.exponent)) USD_amt,
avg(t.AMOUNT * fx.rate / POWER(10, cd.exponent)) monthly_avg,
avg(t.AMOUNT * fx.rate / POWER(10, cd.exponent)) over (partition by t.user_id) user_avg
from
transactions t
join fx_rates fx
on (fx.ccy = t.currency and fx.base_ccy = 'USD')
join currency_details cd
on cd.currency = t.currency
group by
t.user_id,
t.created_date,
month(t.created_date),
year(t.created_date)
having
monthly_avg > 10* user_avg

Related

New column to calculate percentage

Need to add a column with percentage for each "adh_classi" by "stop_code"
ex.
"Stop_code" Count adh_Classi
10013 32 Early
10013 101 Late
10013 317 On-Time
Total for 10013 = 450
Early-> 7.11% (32/450)
Late -> 22.44% (101/450)
I do not have much Access experience
Accomplishing in a query requires an aggregate subquery or DSum() aggregate function to calculate the total for each class.
SELECT Stop_Code, 100 * Count / (SELECT Sum(Count) AS SumCnt FROM tablename AS Q1
WHERE Q1.Stop_Code = tablename.Stop_Code) AS Pct FROM tablename;
or
SELECT tablename.Stop_Code, 100 * Count / SumCnt AS Pct
FROM tablename
INNER JOIN (SELECT Stop_Code, Sum(Count) AS SumCnt FROM tablename
GROUP BY Stop_Code) AS Q1
ON tablename.Stop_Code = Q1.Stop_Code;
or
SELECT Stop_Code, 100 * Count / DSum("Count", "tablename", "Stop_Code=" & [Stop_Code]) AS Pct
FROM tablename
Domain aggregate function causes slower performance in large dataset.
Another approach is to build a report that uses Grouping & Sorting design and aggregate function calc in textbox of group footer: =Sum([Count]) . Expression in detail section would reference footer textbox: =100 * [Count] / [tbxSubTotal].

MYSQL Calculate Conversion Rate

I could manage to return the correct Conversion Rate with this query:
SELECT
date(ordertime),
(count(*) / (
SELECT
sum(uniquevisits)
FROM
s_statistics_visitors
WHERE
datum = '2020-11-25') * 100) AS 'CONVERSION RATE'
FROM
s_order
WHERE
date(ordertime) = '2020-11-25'
AND subshopID = 1
GROUP BY
date(ordertime);
But it only returns the CR for one specific date. It wont work with the between keyword.
The subquery returns more then one result, if I delete the where condition in the subquery.
Schema for s_statistics: id, shopID, datum, uniquevisits, devicetype
Schema for s_order: id, ordernumber, ordertime, shopID
Since s_statistics saves values for each devicetype I have to sum uniquevisits per day. But the group by date(ordertime) at the end of my query does not affect the subquery.
-original post-
I want to calculate the conversion rate for an onlineshop.
The data is held in two tables.
Schema1 statistics: id / shopID / datum(yyyy-mm-dd) / uniquevisits / devicetype
Schema2 order: id / ordernumber / ordertime (YYYY-MM-DD HH-MM-SS)
Upon my knowledge the conversionrate calculates like:
(unique visits per day / 1000) * orders per day
I tried something like this:
SELECT
((count(ordernumber) / 1000) * (select sum(s_statistics_visitors.uniquevisits)
FROM s_statistics_visitors GROUP BY* datum))
FROM s_order where subshopID= '1'
GROUP BY date_format(ordertime, '%d%m%y')
ORDER BY date_format(ordertime, '%d%m%y')
I know that my query wont work - since there is no join - but I believe it might be the right approach. I could you join on the the date.
The problem:
the dateformat in the table: s_statistics_visitors is YYYY-MM-DD
and in order: YYYY-MM-DD HH-MM-SS
You seem to want something like this:
SELECT sv.datum, sv.shopId, count(*) as num_orders,
(count(*) / 1000) / sv.uniquevisits)
FROM s_order o JOIN
s_statistics_visitors sv
ON sv.datum = DATE(o.ordertime) AND
sv.shopId = o.shopId
GROUP BY sv.datum, sv.shopId, sv.uniqevisits ;
Note: This assumes that shopId is in both tables -- which makes sense for how you have described the problem. If not, you can adjust the shop id logic.

MySQL most price change over time

price | date | product_id
100 | 2020-09-21 | 1
400 | 2020-09-20 | 2
300 | 2020-09-20 | 3
200 | 2020-09-19 | 1
400 | 2020-09-18 | 2
I add an entry into this table every day with a product's price that day.
Now I want to get most price drops for the last week (all dates up to 2020-09-14), in this example it would only return the product_id = 1, because that's the only thing that changed.
I think I have to join the table to itself, but I'm not getting it to work.
Here's something that I wanted to return the most price changes over the last day, however it's not working.
select pt.price, pt.date, pt.product_id, (pt.price - py.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
where pt.price - py.price > 0
order by change
I understand that you want to count how many times the price of each product changed over the last 7 days.
A naive approach would use aggregation and count(distinct price) - but it fails when a product's price changes back and forth.
A safer approach is window functions: you can use lag() to retrieve the previous price, and compare it against the current price; it is then easy to aggregate and count the price changes:
select product_id, sum(price <> lag_price) cnt_price_changes
from (
select t.*, lag(price) over(partition by product_id order by date) lag_price
from mytable t
where date >= current_date - interval 7 day
) t
group by product_id
order by price_changes desc
Try using MAX() and MIN() instead....
select MAX(pt.price), MIN(pt.price), MAX(pt.price) - MIN(pt.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
order by change
Instead of subtracting every row by every other row to get the result, you can find the max and min's easily by means of MAX() and MIN(), and, ultimately, **MAX() - MIN()**. Relevant lines from the linked MySQL documentation...
MAX(): Returns the maximum value of expr.
MIN(): Returns the minimum value of expr.
You won't be able to pull the other fields (id's, dates) since this is a GROUP BY() implied by the MAX() and MIN(), but you should then be able to get that info by query SELECT * FROM ... WHERE price = MAX_VALUE_JUST_ACQUIRED.
This examples will get you results per WeekOfYear and WeekOfMonth regarding the lowering of the price per product.
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
WEEK(m1.ddate) AS weekofyear
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofyear,m1.product_id
ORDER BY weekofyear;
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
FLOOR((DAYOFMONTH(ddate) - 1) / 7) + 1 AS weekofmonth
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofmonth,m1.product_id
ORDER BY weekofmonth;
Try this out in SQLFiddle.

How to combine two sql expressions into one?

I have the following issue:
It is necessary to write a query that will output 'item_id', 'price_in_byr'.
'price_in_byr' is calculated as the 'price' of the items table multiplied by the currency rate at the maximum date of the rate from the table rates.
See Schema
I apologize for my English, I'll try to explain by example:
Goods with item_id = 5 costs 20 euros, in the rates table the maximum date for the euro is January 12, at that date the exchange rate was 25. Total our 'price_in_byr' is 25 * 20 = 500
My solution with temp table:
CREATE TABLE tempRate SELECT currency, MAX(rate) AS maxRate FROM rates GROUP
BY currency;
SELECT items.item_id,(ifnull(tempRate.maxRate,1) * items.price) AS price_in_byr
FROM items
LEFT JOIN tempRate ON items.currency = tempRate.currency;
Tell me please, how can I do it in one query?
You can just use a subquery:
SELECT
items.item_id,(ifnull(tempRate.maxRate,1) * items.price) AS price_in_byr
FROM
items
LEFT JOIN
(
SELECT
currency, MAX(rate) AS maxRate
FROM
rates
GROUP BY
currency
) AS tempRate
ON items.currency = tempRate.currency;
In practice, you substitute "tempRate" by (definition of tempRate) AS tempRate.
You can see an example at dbfiddle here
If you actually want the * most_recent_rate*, you'd do something completely different; insert a subquery to compute it. This subquery looks for all rates of the given currency, sorts them by their exchange_ts (meaning timestamp) in descending order, and just picks to top 1:
SELECT
items.item_id,(ifnull(
(
SELECT
rate AS most_recent_rate
FROM
rates
WHERE
rates.currency = items.currency
ORDER BY
exchange_ts DESC
LIMIT 1
)
, 1) * items.price) AS price_in_byr
FROM
items ;
dbfiddle here
You can make your tempRate query into a subquery:
SELECT
items.item_id,
(ifnull(tempRate.maxRate,1) * items.price) AS price_in_byr
FROM
items
LEFT JOIN (
SELECT currency, MAX(rate) AS maxRate
FROM rates
GROUP BY currency
) as tempRate ON items.currency = tempRate.currency;
I am not sure if mysql supports this level of subquery but see if it works:
select I.item_id, I.price * CR.rt
from
items as I,
(
select r.currency cy, r.rate rt
from
rates as r, (select currency, max(date) max_date from rates group by currency) as R_MAXDATES
where
r.currency = R_MAXDATES.currency
and r.date = R_MAXDATES.max_date;
) As CR
where
I.currency = CR.cy

Percent on basis of row count and row count on basis of condition in single table in mysql

I have four tables with the following structure.
Table 1:
Project - have unique project names (prj_name)
Table 2:
my_records - have the following fields:
record_id,prj_name,my_dept,record_submit_date,record_state
Table 3:
record_states have multiple states where 'Completed' is one.
Table 4:custom_dept_list
dept_name
I need to get the percentage of (records have state as completed) and (Total records) grouped by my_project where my_dept in custom_dept_list and record_submit_date is greater than "some date"
I have tried the following:
Query:
select prj_name,count(record_id) as total,((select count(record_id) from
my_records where record_state='Completed')/(count(record_id)))*100 as
percent from my_records,custom_dept_list where record_state='Completed'
and record_submit_date >= ( CURDATE() - INTERVAL 15 DAY ) and
my_dept=dept_name group by prj_name order by percent desc;
Total records for project A = 50
Total records for project A with record_state='Completed' = 30
Ratio is not coming - (30/50)*100 = 60
It is giving some very big value.
Below is the data from my_records, i have removed record_submit date to make it simple:
|1|prj1|dept1|Completed
|2|prj1|dept1|XYZ
|3|prj1|dept1|Completed
|4|prj1|dept2|XYZ
|5|prj1|dept2|Completed
|6|prj1|dept1|XYZ
|7|prj1|dept1|XYZ
|8|prj1|dept1|XYZ
|9|prj1|dept2|XYZ
|10|prj1|dept2|XYZ
|11|prj1|dept2|Completed
|12|prj1|dept2|Completed
|13|prj1|dept2|Completed
|14|prj1|dept3|XYZ
|15|prj1|dept4|Completed
|16|prj1|dept4|XYZ
|17|prj1|dept5|Completed
|18|prj1|dept6|XYZ
|19|prj1|dept7|XYZ
|20|prj1|dept8|XYZ
|21|prj1|dept10|XYZ
|22|prj1|dept2|XYZ
|23|prj1|dept2|Completed
|24|prj1|dept2|Completed
|25|prj1|dept2|Completed
Data From Custom_dept_List:
dept_name
dept1
dept3
dept4
dept5
dept6
dept8
dept10
I have tried the following queries :
Query 1
select count(record_id) as count,prj_name from my_records,custom_dept_list where my_dept=dept_name group by prj_name order by count desc;
Ouput -- 13
Query 2
select count(record_id) as count,prj_name from my_records,custom_dept_list where my_dept=dept_name and record_state='Completed' group by prj_name order by count desc;
Output -- 4
Query 3
select prj_name,count(record_id) as total,count(case when record_state='Completed' then record_id end) /count(record_id) *100 as percent from my_records join custom_dept_list on my_dept = dept_name where record_state = 'Completed' group by prj_name order by percent desc;
Output :
prj_name total percent
prj1 4 100.0000
First of all, please use proper join instead of multiple tables in your from clause.
Then, you don't need that inner query to get the count with a specific record_state, you can use a case inside the count:
select prj_name,
count(record_id) as total,
count(case when record_state='Completed' then record_id end) /
count(record_id) * 100 as percent
from my_records
join custom_dept_list
on my_dept = dept_name
where record_submit_date >= ( CURDATE() - INTERVAL 15 DAY )
group by prj_name
order by percent desc;
Your problem was probably caused by that inner query, that was not counting each project's completed records, but all the completed records instead.
you do not need this record_state = 'Completed' condition because of this you get only completed record as total recoded. so try without it.
select prj_name,
count(record_id) as total,
count(case when record_state='Completed' then record_id end) /
count(record_id) * 100 as percent
from my_records
join custom_dept_list
on my_dept = dept_name
where record_submit_date >= ( CURDATE() - INTERVAL 15 DAY )
group by prj_name