SQL: Group by date, and sum - sql

I have the following table in the database:
date account_id currency balanceUSD
01-01-2022 17:17:25 1 USD 1000
01-01-2022 17:17:25 1 EUR 1200
01-01-2022 23:14:34 1 USD 1050
01-01-2022 23:14:34 1 EUR 1350
01-02-2022 15:14:42 1 USD 1040
01-02-2022 15:14:42 1 EUR 1460
01-02-2022 20:17:45 1 USD 1030
01-02-2022 20:17:45 1 EUR 1550
01-01-2022 17:17:25 2 USD 3000
01-01-2022 17:17:25 2 EUR 2300
01-01-2022 23:14:34 2 USD 3200
01-01-2022 23:14:34 2 EUR 1450
01-02-2022 15:14:42 2 USD 3350
01-02-2022 15:14:42 2 EUR 1850
01-02-2022 20:17:45 2 USD 3400
01-02-2022 20:17:45 2 EUR 1900
What I want to do is group by (year, month, day) and account_id and sum the balanceUSD. i.e.
date account_id balanceUSD
01-01-2022 1 4600
01-02-2022 1 5080
01-01-2022 2 9950
01-02-2022 2 10500
How can this be done?

We can use the function date_trunc('day', rental_date) to extract the date from the timestamp.
SELECT
date_trunc('day', date) as "date",
account_id,
sum(balanceUSD) as "balanceUSD"
FROM
account_id,
table_name
GROUP BY
account_id
date_trunc('day', date)
ORDER BY
account_id,
date_trunc('day', date) ;

Related

Get the amount in two days

I'm trying to write a query that will return the amount of sales given the previous day. I am doing a test task for an internship device but have not done this before.
Source table:
saledate
salesum
2022-01-01
100
2022-01-02
150
2022-01-03
200
2022-01-05
100
Estimated result:
saledate
salesum
2022-01-01
100
2022-01-02
250
2022-01-03
350
2022-01-05
300
My query:
SELECT t1.saledate, t1.salesum=t1.salesum+t2.salesum
FROM sales t1
INNER JOIN (
SELECT saledate, salesum FROM sales
) t2
ON t1.saledate=t2.saledate;
My result:
saledate
salesum
2022-01-01
f
2022-01-02
f
2022-01-03
f
2022-01-05
f
select saledate
,salesum + coalesce(lag(salesum) over(order by saledate),0) as salesum
from t
saledate
salesum
2022-01-01
100
2022-01-02
250
2022-01-03
350
2022-01-05
300
Fiddle

Calculate Average for Amount for certain date range in a year based on month

I have a table like below :
ID
Amount
Date
1
500
2022-01-03
1
200
2022-01-04
1
500
2022-01-05
1
340
2022-01-06
1
500
2022-01-25
1
500
2022-01-26
1
567
2022-01-27
1
500
2022-01-28
1
598
2022-01-31
1
500
2022-02-01
1
787
2022-02-02
1
500
2022-02-03
1
5340
2022-02-04
PROBLEM :-
So I have to calculate average of column where StartDate = 03/01/2022 (3rd Jan 2022) and for each month it would be like for January Average of Amount from StartDate to 25th Jan, then for Feb Startdate to 22nd Feb, so this date logic is also there
SET #Last = (SELECT DATEADD(DAY, CASE DATENAME(WEEKDAY, #Date)
WHEN 'Sunday' THEN -6
When 'Saturday' THEN -5
ELSE -7 END, DATEDIFF(DAY, 0, #Date)))
RETURN #Last
ID
Amount
Date
Last
1
500
2022-01-03
2022-01-25
1
500
2022-01-04
2022-01-25
1
340
2022-01-05
2022-01-25
1
500
2022-01-06
2022-01-25
1
567
2022-01-25
2022-01-25
1
500
2022-01-26
2022-01-25
1
500
2022-01-27
2022-01-25
1
40
2022-01-28
2022-01-25
1
500
2022-01-31
2022-01-25
1
589
2022-02-01
2022-02-22
1
540
2022-02-02
2022-02-22
1
500
2022-02-03
2022-02-22
1
5340
2022-02-04
2022-02-22
Like the above table..
Now if I calculate Avg(Amount), from 3rd jan to 25th Jan for Jan and 3rd Jan to 22nd Feb and so on.. It's not giving correct average, like it is calculating the rest of the days amount also. Also grouping by is grouping month wise not as where clause
Select Avg(Amount) from Table
where Date BETWEEN #StartDate AND Last
StartDate is fixed # 3rd Jan.
This is not giving the correct Avg. Any other way I could get the required data?

How to split months between a start date and end date from a table in SQL

I want to set up a stored procedure in SQL Server which creates month by month currency exchange rates based on a start and end date per currency.
The following is the format of the table I want to work with:
Table 1 (Input Table)
+------------+------------+------------+
| Start Date | End Date | Currency |
+------------+------------+------------+
| 01/01/2016 | 30/05/2016 | EUR |
| 01/03/2017 | 31/05/2017 | BDT |
+------------+------------+------------+
From the above table, I want the stored procedure to give an output like this:
Table 2 (Desired result from sql script)
Date Currency
---------------------
01/01/2016 EUR
01/02/2016 EUR
01/03/2016 EUR
01/04/2016 EUR
01/05/2016 EUR
01/03/2017 BDT
01/04/2017 BDT
01/05/2017 BDT
Then I want to join these two outputs to give a final table like this:
Final Table (Join on Table 1 and 2)
Start Date End Date Split Date Currency Exchange Rate
-------------------------------------------------------------
01/01/2016 30/05/2016 18/01/2016 EUR x
01/01/2016 30/05/2016 18/02/2016 EUR z
01/01/2016 30/05/2016 18/03/2016 EUR h
01/01/2016 30/05/2016 18/04/2016 EUR g
01/01/2016 30/05/2016 18/05/2016 EUR a
01/03/2017 31/05/2018 01/03/2017 BDT b
01/03/2017 31/05/2018 01/04/2017 BDT c
01/03/2017 31/05/2018 01/05/2017 BDT f
I have found some solutions on stackoverflow like this:
declare #StartDate date = '20170401'
, #EndDate date = '20170731';
;with Months as
(
select top (datediff(month, #startdate, #enddate) + 1)
[Month] = dateadd(month, row_number() over (order by number) -1, #StartDate),
MonthEnd = dateadd(day,-1,dateadd(month, row_number() over (order by number), #StartDate))
from
master.dbo.spt_values
order by
[Month]
)
select * from Months;
However, this only uses hard coded start and end dates. I want the start and end dates to be taken per row from the an input table like the one mentioned at the beginning of the question.
I think I have got the idea of what you are trying to do. This will do it all in one query.
First set up your test data (correcting your typo where enddate was < startdate)
create table xchg(startdate date, enddate date, currency varchar(3))
insert xchg values ('2016-01-18','2016-05-30','EUR')
,('2017-03-19','2017-05-31','BDT')
Then a recursive query picking out each anniversary between the two dates. Don't know where you are getting the exchange rate from, but you should be able to add it to the this.
;with splits as
(
select *, startdate as split from xchg
union all
select startdate, enddate, currency,
dateadd(m,1,split)
from splits
where dateadd(m,1,split) <= enddate
)
select * from splits order by currency, split
Result is:
startdate enddate currency split
2017-03-19 2017-05-31 BDT 2017-03-19
2017-03-19 2017-05-31 BDT 2017-04-19
2017-03-19 2017-05-31 BDT 2017-05-19
2016-01-18 2016-05-30 EUR 2016-01-18
2016-01-18 2016-05-30 EUR 2016-02-18
2016-01-18 2016-05-30 EUR 2016-03-18
2016-01-18 2016-05-30 EUR 2016-04-18
2016-01-18 2016-05-30 EUR 2016-05-18

How to link two tables but only take the MAX value from one table in PostgreSQL?

I have two tables
exchange_rates
TIMESTAMP curr1 curr2 rate
2018-04-01 00:00:00 EUR GBP 0.89
2018-04-01 01:30:00 EUR GBP 0.92
2018-04-01 01:20:00 USD GBP 1.23
and
transactions
TIMESTAMP user curr amount
2018-04-01 18:00:00 1 EUR 23.12
2018-04-01 14:00:00 1 USD 15.00
2018-04-01 01:00:00 2 EUR 55.00
I want to link these two tables on 1. currency and 2. TIMESTAMP in the following way:
curr in transactions must be equal to curr1 in exchange_rates
TIMESTAMP in exchange_rates must be less than or equal to TIMESTAMP in transactions (so we only pick up the exchange rate that was relevant at the time of transaction)
I have this:
SELECT
trans.TIMESTAMP, trans.user,
-- Multiply the amount in transactions by the corresponding rate in exchange_rates
trans.amount * er.rate AS "Converted Amount"
FROM transactions trans, exchange_rates er
WHERE trans.curr = er.curr1
AND er.TIMESTAMP <= trans.TIMESTAMP
ORDER BY trans.user
but this is linking on two many results as the output is more rows than there are in transactions.
DESIRED OUTPUT:
TIMESTAMP user Converted Amount
2018-04-01 18:00:00 1 21.27
2018-04-01 14:00:00 1 18.45
2018-04-01 01:00:00 2 48.95
The logic behind the Converted Amount:
row 1: user spent at 18:00 so take the rate that is less than or equal to the TIMESTAMP in exchange_rates i.e. 0.92 for EUR at 01:30
row 2: user spent at 14:00 so take the rate that is less than or equal to the TIMESTAMP in exchange_rates i.e. 1.23 for USD at 01:20
row 3: user spent at 01:00 so take the rate that is less than or equal to the TIMESTAMP in exchange_rates i.e. 0.89 for EUR at 00:00
How can I do this in postgresql 9.6?
You can use a LATERAL JOIN (CROSS APPLY) and limit the result to the first row that match your conditions.
select t.dt, t.usr, t.amount * e.rate as conv_amount
from transactions t
join lateral (select *
from exchange_rates er
where t.curr = er.curr1
and er.dt <= t.dt
order by dt desc
limit 1) e on true;
dt | usr | conv_amount
:------------------ | --: | ----------:
2018-04-01 18:00:00 | 1 | 21.2704
2018-04-01 14:00:00 | 1 | 18.4500
2018-04-01 01:00:00 | 2 | 48.9500
db<>fiddle here

Update table by join

I have a exchange rate table like below
FromCurrency ToCurrency ValidFrom ExchangeRate
USD ZAR 2012-01-05 00:00:00.000 7.7260000000
USD ZAR 2012-01-04 00:00:00.000 7.6740000000
USD ZAR 2012-01-03 00:00:00.000 7.4601000000
USD ZAR 2012-01-02 00:00:00.000 7.7600000000
USD ZAR 2012-01-01 00:00:00.000 8.0945000000
EUR NOK 2012-01-05 00:00:00.000 7.5881000000
EUR NOK 2012-01-04 00:00:00.000 7.5974000000
EUR NOK 2012-01-03 00:00:00.000 7.4494000000
EUR NOK 2012-01-02 00:00:00.000 7.6606000000
EUR NOK 2012-01-01 00:00:00.000 7.7740000000
USD AED 2012-01-05 00:00:00.000 3.6731000000
USD AED 2012-01-04 00:00:00.000 3.6731000000
My main table is
Transaction Date Amount Currency FromCurrency
13971 5/27/2011 8000 USD USD
13971 7/31/2011 -6809.4 ZAR USD
13971 8/30/2011 -425.59 ZAR USD
13971 9/27/2011 -6809.4 ZAR USD
67467 11/8/2011 5000 GBP GBP
67467 12/21/2011 -5195.06 ZAR GBP
67467 1/30/2012 -5195.06 ZAR GBP
81181 4/15/2011 6000 USD USD
81181 6/28/2011 -5159.03 ZAR USD
82418 10/21/2011 5000 EUR EUR
82418 1/27/2012 -3919.97 NOK EUR
Above is sample data of table, my actual table has millions of records. I have to multiply amount with exchange rate column (from exchange rate table) and update the corresponding FromCurrency (from exchange rate table) with currency column in my table.
I have tried many ways but couldn't find the solution. The tricky part here is there is not matching date for my table in exchange rate table.
Exchange rate should be applied like this. If my transaction date is Feb-20-2012 and Currency is ZAR and FromCurrency is USD then I have to pick the below row from exchange rate table
USD ZAR 2012-01-02 00:00:00.000 7.7600000000
update transactions
set FromCurrencyAmount = transactions.amount *
(select top 1 exchangerate from exchangerates where validfrom <= transactions.date
and transactions.fromcurrency = exchangerates.fromcurrency
and transactions.currency = exchangerates.tocurrency
order by validfrom desc)
To select
select transactions.*,
(select top 1 exchangerate from exchangerates where validfrom <= transactions.date
and transactions.fromcurrency = exchangerates.fromcurrency
and transactions.currency = exchangerates.tocurrency
order by validfrom desc)
from transactions
Another option is :
select * from
transact t1 join exchRate e1 on t1.FromCurrency = e1.FromCurrency
and t1.ToCurrency = e1.ToCurrency
and t1.tdate > e1.ValidFrom
and not exists
(select 1 from exchRate where ValidFrom < t1.tdate and ValidFrom > e1.ValidFrom)
I hope understand
SELECT T3.*,
T4.DATEMAX,
T4.VALUESELECTED
FROM TRANSACTIONS AS T3
INNER JOIN
(SELECT T.CURRENCY,
T.FROMCURRENCY,
T.DATEMAX,
T2.EXCHANGERATE AS VALUESELECTED
FROM (SELECT CURRENCY,
FROMCURRENCY,
MAX(ValidFrom) AS DATEMAX
FROM ExChangeRate
WHERE ValidFrom <= T3.DATE
GROUP BY Currency, FromCurrency) AS T
INNER JOIN
ExChangeRate AS T2
ON T.CURRENCY = T2.CURRENCY
AND T.FROMCURRENCY = T2.FROMCURRENCY
AND T.DATEMAX = T2.VALIDFROM) AS T4
ON T4.CURRENCY = T3.CURRENCY
AND T4.FROMCURRENCY = T3.FROMCURRENCY;