How to create dynamic time intervals in SQL Snowflake? - sql

I am writing a query that should recognize the dates when different budgets associated to one account were totally consumed. My start date is 1/12 so my task is to create a cumulative sum of the expenses that the account made for its first budget, then when the cumulative sum reach the budget, I should store the date when it happened, and the use that date as my new START DATE to compute the expenses of the same account but for the second budget, and so on. It seems for me difficult to create this dynamic interval was on the result of the previous budget. Do you have any idea of how I could I manage this problem?
What I Tried
WITH TRM_INTERVAL AS
(
SELECT
Country,
replace(Account_ID, '-', '') AS fixed_accountid,
TO_DATE(Date) AS CONVERTED_DATE,
TO_DATE(CAST(CONVERTED_DATE AS VARCHAR), 'YYYY-DD-MM') AS CONVERTED_DATE_FORMAT,
TRM2,
TRM1,
TOTAL_ARS_IN_ACCOUNT,
row_number()
over (partition by Account_ID, Country order by CONVERTED_DATE_FORMAT) AS ROW_NUMBER,
lag(TOTAL_ARS_IN_ACCOUNT)
over (partition by Account_ID, Country order by CONVERTED_DATE_FORMAT) AS PREVIOUS_ARS_BUCKET,
lead(CONVERTED_DATE_FORMAT)
over (partition by Account_ID, Country order by CONVERTED_DATE_FORMAT) AS NEXT_DATE
FROM CONECTOR
),
--DETAIL
DETAIL AS (
SELECT TI.COUNTRY, TI.fixed_accountid, TI.PREVIOUS_ARS_BUCKET, TI.TOTAL_ARS_IN_ACCOUNT, TI.ROW_NUMBER, GO.EVENT_DATE, SUM(CASE WHEN GO.CURRENCY_ORIGINAL = 'ARS' THEN GO.FINAL_CAC * 160 END) AS ARS_SPEND,
SUM(ARS_SPEND) OVER(PARTITION BY TI.fixed_accountid, TI.COUNTRY, TI.TOTAL_ARS_IN_ACCOUNT ORDER BY GO.EVENT_DATE ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CUMULATIVE_ARS_SPEND
FROM TABLE1 AS GO
JOIN CONECTOR AS TI
ON GO.ACCOUNT_ID = TI.fixed_accountid AND GO.COUNTRY = TI.COUNTRY
WHERE GO.EVENT_DATE >= '2022-12-1'
GROUP BY 1,2,3,4,5,6
),
--ACUMULADO
LOGIC AS (
SELECT *,
CUMULATIVE_ARS_SPEND / TOTAL_ARS_IN_ACCOUNT AS CUMULATIVE_ARS_SPEND_PERCENTAGE,
--COMO VALIDAR CON EL TI TOTAL ARS CORRESPONDIENTE
CASE WHEN CUMULATIVE_ARS_SPEND_PERCENTAGE >=0.686 AND EVENT_DATE <> CURRENT_DATE() THEN 'FLAG' END AS FLAGS
FROM DETAIL
)
I have 3 subqueries. To sum up, I create the cumulative sum of the expenses related to each account but I could not create the dynamic intervals. The dates start from 12/1/2022.
What actually resulted
You see 3 budgets as example. All of them are associated with a single account. I start with the budget 500,000, you see that I made the cumulative sum until it reach 100%. but then for the second budget 30,000,000 the dates and the expenses are repeated. The same dates were applied 1/12, 1/13, 1/14 and 1/15. The idea is that if the first budget was totally consumed on 1/15 as you can see, the query should compute the expenses for the second budget since one day later 1/16 until that second budget is as well totally consumed and so on.
FECHA BUDGET SPEND_DAILY CUMULATIVE % ACUMM
01/12/2022 500000 100000 100000 20.00%
01/13/2022 500000 150000 250000 50.00%
01/14/2022 500000 200000 450000 90.00%
01/15/2022 500000 50000 500000 100.00%
01/12/2022 30000000 100000 100000 0.33%
01/13/2022 30000000 150000 250000 0.83%
01/14/2022 30000000 200000 450000 1.50%
01/15/2022 30000000 50000 500000 1.67%
01/12/2022 4000000 100000 100000 2.50%
01/13/2022 4000000 150000 250000 6.25%
01/14/2022 4000000 200000 450000 11.25%
01/15/2022 4000000 50000 500000 12.50%
What I expected to happen
The desire output should be these table, where the first budget is totally consumed one day, and on the next day, the expenses compute is updated and now the cumulative sum is applied to the second budget.
FECHA BUDGET GASTO DIARIO GASTO ACUMM % ACUMM
01/12/2022 500000 100000 100000 20.00%
01/13/2022 500000 150000 250000 50.00%
01/14/2022 500000 200000 450000 90.00%
01/15/2022 500000 50000 500000 100.00%
01/16/2022 30000000 100000 100000 0.33%
01/17/2022 30000000 200000 300000 1.00%
01/18/2022 30000000 500000 800000 2.67%
01/19/2022 30000000 300000 1100000 3.67%
01/20/2022 30000000 150000 1250000 4.17%
01/21/2022 30000000 20000000 21250000 70.83%
01/22/2022 30000000 10000000 31250000 104.17%

Related

Slab based calculation in SQL

I want to calculate the slab based logic.
This is my table where the min_bucket and max_bucket range is mention and also the rate of it.
slabs min_bucket max_bucket rate_per_month
----------------------------------------------
Slab 1 0 300000 20
Slab 2 300000 500000 17
Slab 3 500000 1000000 14
Slab 4 1000000 13
We need to calculate as
If there are 450k subs, the payout will be 300k20 + 150k17
If the Total Count is 1000001, then its output should be as
min_bucket max_bucket rate_per_month Count rate_per_month revenue
-----------------------------------------------------------------------
0 300000 20 300000 20 6000000
300000 500000 17 500000 17 8500000
500000 1000000 14 200001 14 2800014
Where count is calculated as 300000+500000+200001 = 1000001, and revenue is calculated as rate_per_month * Count as per the slab.
Can anyone help me write the SQL query for this, which will handle all the cases?
You can build running totals of the slabs table and work with them:
with given as (select 1000001 as value)
, slabs as
(
select
slab,
min_bucket,
max_bucket,
rate_per_month,
sum(min_bucket) over (order by min_bucket) as sum_min_bucket,
sum(coalesce(max_bucket, 2147483647)) over (order by min_bucket) as sum_max_bucket
from mytable
)
select
slabs.slab,
slabs.min_bucket,
slabs.max_bucket,
slabs.rate_per_month,
case when slabs.sum_max_bucket <= given.value
then slabs.max_bucket
else given.value - sum_min_bucket
end as used,
case when slabs.sum_max_bucket <= given.value
then slabs.max_bucket
else given.value - sum_min_bucket
end * slabs.rate_per_month as revenue
from given
join slabs on slabs.sum_min_bucket < given.value
order by slabs.min_bucket;
I don't know your DBMS, but this is standard SQL and likely to work for you (either right away or with a little tweak).
Demo: https://dbfiddle.uk/?rdbms=postgres_13&fiddle=9c4f5f837b6167c7e4f2f7e571f4b26f

SQL Server: Calculate amortization

Currently i'm struggling to solve following problem:
I have two tables (contract & amortization).
Contract: contract informations with an outstanding amount
Amortization: date of payment & amortization amount
My desired result is a table with the contract and the decreasing
oustanding amount.
;WITH CTE AS
(
SELECT con.ID
,con.outstanding
,amo.amortization
,amo.amo_date
,ROW_NUMBER() OVER ( PARTITION BY con.ID
ORDER BY amo.amo_date asc
) as rn
FROM contract con
INNER JOIN amort amo
ON amo.contractID = con.ID
)
SELECT ID
,outstanding
,outstanding - amortization as new_outstanding
,amo_date
FROM CTE
Currently i am getting this result - which is of course wrong, as just one amortization is calculated for the new_outstanding:
ID outstanding new_outstanding amo_date
1 100000 90000 01.08.2017 00:00:00
1 100000 80000 01.09.2017 00:00:00
1 100000 50000 01.10.2017 00:00:00
1 100000 90000 01.11.2017 00:00:00
1 100000 90000 01.12.2017 00:00:00
My desired result would be:
ID outstanding new_outstanding amo_date
1 100000 90000 01.08.2017 00:00:00
1 100000 70000 01.09.2017 00:00:00
1 100000 20000 01.10.2017 00:00:00
1 100000 10000 01.11.2017 00:00:00
1 100000 0 01.12.2017 00:00:00
Any simple idea to solve this in an easy way?
Rextester: http://rextester.com/SBLT77863
Thanks in advance!
I think you just need a cumulative sum:
SELECT con.ID,
con.outstanding,
amo.amortization,
amo.amo_date,
(con.outstanding -
sum(amo.amortization) over (partition by amo.contractId
order by amo.amo_date)
) as new_outstanding
FROM contract con INNER JOIN
amort amo
ON amo.contractID = con.ID;

How to add serial number to a range query in SQL?

I am selecting records within different ranges with a select statement like
select t.range as profit_range, count(*) as number_ of_occurences from (
select case
when t.range between 100000000 and 200000000 then '100 million - 200 million'
when t.range between 200000000 and 300000000 then '200 million - 300 million'
.
.
.
when t.range between 1000000000 and 2000000000 then '1000 million - 2000 million'
else 'Greater then 2000 milions' end as range
from tbl_profit) t
group by profit_range
This works but the order of the resulting rows is not correct
Profit_Range No_of_occurences
100 million - 200 million 4
1000 million - 2000 million 1
200 million - 300 million 4
How do I order the results to appear in the right order?
select t.range as [profit range], count(*) as [number of occurences]
from (
select case
when profit between 100000000 and 200000000 then '100 million - 200 million'
when profit between 200000000 and 300000000 then '200 million - 300 million'
.
.
.
when profit between 1000000000 and 2000000000 then '1000 million - 2000 million'
else 'Greater then 2000 milions' end as range
from tbl_profit) t
group by t.range
Order by SUBSTR(t.range,1,LOCATE(‘million’,t.range)-1)
You can extract starting number from range using SUBSTR() and LOCATE functions and order result according to extracted number

sum without duplicates RDLC

I have a report in RDLC. I try to sum up without duplicates. So how to sum ignoring duplicates??
№ sale Date_Sales Amount Date_payment Amount
01-01 01/07/2013 1000 15/07/2013 500
01-01 01/07/2013 1000 18/07/2013 500
Total 2000 1000
and should be:
№ sale Date_Sales Amount Date_payment Amount
01-01 01/07/2013 1000 15/07/2013 500
01-01 01/07/2013 1000 18/07/2013 500
Total 1000 1000
I hide duplicates in the second line but I can't avoid the sum of this duplicate fields. I need to see the sum of sales and the sum of payments in the same report.
In SQL it would be
SELECT SUM(columnname) AS iSum FROM(
SELECT DISTINCT columnname FROM tablename) AS foo

Get the Data horizontally rather than vertically

Consider the query :
Select Dealer, Sales from DEALERDATA
where period in (201106,201107)
This will give result as:
DEALER SALES
Dealer1 100000
Dealer1 200000
Dealer2 600000
Dealer2 700000
I want result as
DEALER SALES SALES
Dealer1 100000 200000
Dealer2 600000 700000
If any one can let me know how this can be achieved?
Many thanks,
Sharon....
For a few number of columns you can use CASE combined WITH sum
SELECT Dealer
, SUM(CASE WHEN period=201106 THEN Sales ELSE 0 END) sales201106
, SUM(CASE WHEN period=201107 THEN Sales ELSE 0 END) sales201107
FROM DEALERDATA
WHERE period IN (201106,201107)
GROUP BY Dealer