Mysql select result in one currency - mysql

I have to create a reports in one currency. I need to do query in MySQL without using PHP process. but unable to figure it out.
There is a table called currency_exchange_rate table as follows, (exchange rate in LKR to other currency).this table is updating like one record for each currency in LKR in every month
exchange_rates
id currency_id start_date exchange_rate
1 5 2017-01-2 155
2 4 2017-01-3 25
3 6 2017-01-3 53
4 5 2017-02-1 156
5 4 2017-02-1 24
6 6 2017-02-1 54
There is a project table as follows
pro_id name value currency_id status_id owner_id date
1 studio1 500 5 1 44 2017-01-20
2 lotus 120 5 1 42 2017-01-21
3 auro 300 4 2 45 2017-01-21
4 studio2 400 6 1 44 2017-01-22
5 holland 450 4 3 46 2017-02-05
6 studio3 120 4 3 47 2017-02-06
7 studio4 400 6 3 48 2017-02-06
how to generate reports in one currency(DKK but exchange rate in LKR) like status wise,monthly total, total by owner, etc..
and we have to consider currency id,currency to be convert and exchange rate for the month for those currency types to get relevant value for project row.
hope you are clear about my scenario. your help is much appreciated.
I don't need every report. just want a sql for convert values in project table using exchange rates table or status wise report as follows
status_id value_in_one_currency
1 xxxx
2 xxxx
3 xxxx

Try this:
SELECT A.status_id, A.`value` * B.exchange_rate `value_in_one_currency`
FROM project A JOIN exchange_rates B
ON A.currency_id=C.currency_id
AND DATE_FORMAT(A.`date`,'%m-%Y')=DATE_FORMAT(B.`start_date`,'%m-%Y');
See MySQL Join Made Easy for some insight.

This is what I finalize:
I took currency_id=5 as the final currency to be converted
SELECT A.*,C.exchange_rate AS DKK,D.exchange_rate AS LKR, (order_value * D.exchange_rate /C.exchange_rate ) AS `converted_value`
FROM projects A
LEFT JOIN exchange_rates C ON (DATE_FORMAT(C.start_date,'%Y-%m')=DATE_FORMAT(A.`date`,'%Y-%m') AND C.currency_id=5)
LEFT JOIN exchange_rates D ON DATE_FORMAT(D.start_date,'%Y-%m')=DATE_FORMAT(A.`date`,'%Y-%m') AND D.currency_id=A.currency_id

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.

Select weekly average of user usage, only for some users (mysql)

I have 2 tables, and I want to show a weekly TOTAL average of data usage for users who started using the application 10 weeks ago. (in that week)
Table 1 is called "users"
user_id user_name user_date
1 a 2020-05-01
2 b 2020-05-03
3 c 2020-06-01
4 d 2020-06-06
5 e 2020-06-09
Table 2 is called "data_tbl"
data_id user_id date_used data_used
1 1 2020-05-09 7
2 1 2020-05-09 12
3 2 2020-05-12 100
4 2 2020-05-20 177
5 1 2020-05-21 78
6 2 2020-05-29 33
7 1 2020-06-01 44
8 2 2020-06-01 123
9 1 2020-06-03 62
Consider 10 weeks ago is between 2020-05-01 and 2020-05-08
So the 2 users we are interested in in that case is user_id 1 and 2 (a and b)
We consider first week from 05-01 to 05-08
Second week from 2020-05-08 to 2020-05-15
Third week from 2020-05-15 to 2020-05-22
Forth week from 2020-05-22 to 2020-05-29 and so on
For week 1 we would have average usage = 0
For week 2 we would have average usage (7+12+100)/3=39
For week 3 we would have average usage (177+78)/2=127
For week 4 we would have average usage 33
For week 5 we would have average usage (44+123+62)/3=76
I really don't know how to start, if I should do a join, or a select in select with average.
I tested something like: (but no success)
SELECT AVG(data_used),
FROM data_tbl
LEFT JOIN users ON data_tbl.user_id=users.user_id
WHERE users.user_date>= "2020-05-01" AND users.user_date<="2020-05-08"
GROUP BY date
ORDER BY date;
You can achieve this easily with YEARWEEK() function
However what you want to achieve is not totally clear for me because the results you want don't really match your data.
Example:
SELECT YEARWEEK(SYSDATE()) AS Actual_Week,
YEARWEEK(user_date) User_Date_Week,
YEARWEEK(SYSDATE()) - YEARWEEK(user_date) AS diff_weeks ,
u.*
FROM users u
Returns
Actual_Week User_Date_Week diff_weeks user_id user_name user_date
202029 202017 12 1 a 2020-05-01
202029 202018 11 2 b 2020-05-03
202029 202022 7 3 c 2020-06-01
202029 202022 7 4 d 2020-06-06
202029 202023 6 5 e 2020-06-09
So you can see that user 1 is 12 weeks ago, and user 2 is 11 week ago. And you assume they are 10 weeks ago, which is incorrect. Sames goes with your date_used in data_tbl.
So I'll just put you on the right path, it should then be easy to adapt following your needs...
Do something like this
SELECT YEARWEEK(d.date_used), AVG(data_used)
FROM users u
INNER JOIN data_tbl d ON u.user_id = d.user_id
WHERE (YEARWEEK(SYSDATE()) - YEARWEEK(u.user_date)) BETWEEN 11 AND 12
GROUP BY YEARWEEK(d.date_used)
Returns
YEARWEEK(d.date_used) AVG(data_used)
202018 9.5
202019 100
202020 127.5
202021 33
202022 76.3333
You can see that the numbers you expect are there, but that they are others. And this result seems correct to me, the results in your question were wrong.
Notice that to get the results for user 1 and 2, I specified
WHERE (YEARWEEK(SYSDATE()) - YEARWEEK(u.user_date)) BETWEEN 11 AND 12
If you want the user of 10 weeks ago, just do
WHERE (YEARWEEK(SYSDATE()) - YEARWEEK(u.user_date)) = 10
And to conclude :
you might want to change the mode of YEARWEEK(), if the weeks should start on Monday, Sunday, or other options. Modes are well described here, with plenty of examples
If you also want the weeks without data in your results (so always 0), you have to use a Calendar table. There are plenty of examples on SO.

Is there a way to have a conditional statement for comparing of tables using MySQL only?

I am having a dilemma from comparing my tables.
My problem is, I want to get each sum, which depends on the pricing. Here is the table.
Main table
main_id main_price main_date_created
25 8.5 2019-08-16
26 11.5 2019-08-01
Total table
id main_id total_price date_generated
1 25 10 2019-08-16
2 25 10 2019-08-17
3 25 10 2019-08-18
4 25 10 2019-08-19
5 25 10 2019-08-20
6 25 10 2019-08-21
7 26 20 2019-08-01
8 26 5 2019-08-02
9 26 5 2019-08-03
10 26 10 2019-08-04
Price History table
id main_id changed_main_price price_date_changed
1 25 15 2019-08-18
2 26 20 2019-08-03
I don't know if there is a way to do this just by using MySQL, what I am trying to achieve is, the Total table will be sum by MONTH and YEAR and will be multiplied by their designated price that depends on the date whether if the price has changed or not . the SUM will from each month will be generated by multiplying with the main price in the Main table but if the price had changed from its original price which it is on the Price history table
The output should be like this if the conditional is possible:
id main_id total_price price_generated (which is the prices) date
1 25 170 (10+10*8.5) 8.5
2 25 610 (10+10+10+10*15) 15
3 26 287.5 (20+5*11.5) 11.5
4 26 300 (5+10*20) 20
Here is my existing query,
SELECT m.main_id
, m.main_price
, SUM(t.total_price) total_generated
, t.date_generated
FROM main m
INNER JOIN total t
ON m.main_id = t.main_id
GROUP
BY MONTH(t.date_generated);
I know that my query is not enough, and I still don't know if my idea is really possible :(.
I racked my brain. hahaha Is this you're after?
SELECT pricelist.id, pricelist.price, SUM(t.total_price), SUM(t.total_price) * pricelist.price,
year( pricelist.latestdate), month(pricelist.latestdate),
year( t.total_date_generated), month(t.total_date_generated)
from totaltable as t
LEFT JOIN (
(
select main_id as id, main_price as price, main_date_created as latestdate
from maintable
)
UNION ALL
(
select total_main_id, changed_main_price , price_date_changed
from pricehistorytable
)
) as pricelist on pricelist.id = t.total_main_id
GROUP BY pricelist.id , t.total_price, pricelist.price,
year( pricelist.latestdate), month(pricelist.latestdate),
year( t.total_date_generated), month(t.total_date_generated) ;

How to Sum a single Column based on grouping in MySQL

I'm having a Two Tables one is Sales and another one is BrandMaster
My Requirement : How to Sum a single Column based on grouping in MySQL
The Structure and Data of Sales Table:
SNo BID Amount
-----------------------------------
101 1 200
102 2 500
103 5 800
104 8 250
105 1 200
106 2 500
107 5 800
108 8 250
The Structure and Data of BrandMaster Table:
BID BrandName
-------------------------
1 Prod#1
2 Prod#2
3 Prod#3
4 Prod#4
5 Prod#5
6 Prod#6
7 Prod#7
8 Prod#8
My Expected Output:
BID SumAmount
-------------------------
1 400
2 500
3 0
4 0
5 0
6 1600
7 0
8 500
The Sales Table contains the BrandID 'BID' and the Sales Amount with Sales ID 'SNo'. I need the Sum of Sales Amount for each Product. Kindly assist me.
You can try this
SqlFiddle Demo
SELECT
BrandMaster.BID,
IFNULL(SUM(Sales.Amount),0) AS SumAmount
FROM BrandMaster
LEFT JOIN Sales ON ( BrandMaster.BID=Sales.BID )
GROUP BY
BrandMaster.BID
ORDER BY
BrandMaster.BID,
Sales.SNo
Here you go!
SELECT db.sales.bid AS "BID",
<AGGREGATE>(db.sales.amount) AS "SumAmount"
FROM db.sales
GROUP BY db.sales.bid
ORDER BY db.sales.bid;
Edit: db is your database, and sales is your, "Sales," table.
Second Edit: replace <AGGREGATE> with something from here.

MySQL Select Last n Rows For List of ID'S

Fixture Table
uid home_uid away_uid winner date season_division_uid
1 26 6 6 2013-07-30 18
2 8 21 8 2013-06-30 18
3 6 8 8 2013-06-29 18
4 21 26 21 2013-05-20 18
5 6 26 6 2013-04-19 18
This table contains hundreds of rows.
Currently I have a query to select all the teams in a division, i.e.
SELECT team_uid
FROM Season_Division_Team
WHERE season_division_uid='18'
which lists the rows of team uid's i.e. [6,26,8,21,26].
Now for each of the unique team ids, I would like to return the last 3 winner values, ordered by the date column, that they were involved in (they could be an away_uid or home_uid).
So the returned value example would be:
team_id winner date
6 6 2013-07-30
6 8 2013-06-29
6 26 2013-04-19
26 6 2013-07-30
26 21 2013-05-20
26 6 2013-04-19
Any ideas? Thank you
Im not sure how to get it direct, a query like
select * from Season_division_Team where
`date >= (select min(`date`) from
(select `date` from season_division_team order by date desc limit 3))
and (home_uid = 6 or away_uid = 6)
Thats not going to be a good query. But only way i can think of currently
Its hard to get the 3rd largest value from SQL Example
the sub query is trying to get the date where the last win occured, and then getting all dates after that where the team played.
EDIT:
SELECT * FROM Season_Division_Team WHERE winner = 6 ORDER BY `date` DESC LIMIT 3
that sounds more like your latter comment