How to find first day of multi-day event on monthly basis? - sql

I have a table with daily snapshot data that has a date and amount column. I'm converting this to a monthly snapshot table. Every record in this monthly table should look like what you see in the table below at 31-01.
If on the last day of the month the amount is negative, amount_negative_lastday_of_month = True. The part I can't figure out is the first_date_negative_amount. If the amount_negative_lastday_of_month = True, I want to get the first day on which the amount became negative for that specific period where the amount was negative. In the table below that would be 28-01-2021.
If this period of amount < 0 continuous on until the end of February, we will see the exact same in the Feb. monthly snapshot where amount_negative_lastday_of_month = True and amount_negative_lastday_of_month = 28-01-2021.
If this period of negative amount is over, and in a few months a new period of negative amount begins, I want to do the same again.
The problem is that I can't figure out how to group/window my data by varying periods of time where a certain condition is met (amount < 0).
How can I query this?
date
amount
last_day_of_month
amount_negative_lastday
first_date_negative_amount
26-1-2021
-10
27-1-2021
200
28-1-2021
-200
29-1-2021
-200
30-1-2021
-200
31-1-2021
-100
31-1-2021
TRUE
28-1-2021
1-2-2021
-122
2-2-2021
-222
3-2-2021
-322

Related

Forecast based on monthly figures

I receive a monthly list of unpresented cheques with payment date. Those reached certain age in Month1 (say, 90 days to date) are recognized as overdue and counted as X(1).
Those of > 60 days age Y(1) are near-overdue and will appear in Month2 statement as X(2), if not banked by then.
Some of cheques arу pretty old and reside in the system for ages (over 1000 days), and therefore appear in each X monthly statement, while certain share of cheques in both X(n) and Y(n) will disappear from next month's X(n+1).
What would be the best logic for next month forecast basing on actual historical data? The most importand is X, but Y also welcome. It should be a forecast, as there is no next month's data as of yet.
The data is in SQL if it is relevant, but I need to understand the logic most, then I can generate code.
The logic below gets you the Number of Checks, Percent Overdue, Percent Near Overdue, and Percent All Other Checks counts for the current time period. Next step would be to create a query that does this for 3 or 12 months (or however many months you want to use to get projections). Then you trend out your total number of checks, and trend the percentages for each of the subcategories. You can use that to predict future amounts.
SELECT
Count(*) AS Number of Checks,
(SUM(CASE WHEN s.ageofcheck >= 90 THEN 1 ELSE 0 END))/Count(*) AS Number of Checks AS Percent Overdue Checks,
(SUM(CASE WHEN s.ageofcheck >= 60 AND s.ageofcheck < 90 THEN 1 ELSE 0 END))/Count(*) AS Number of Checks AS Percent Near Overdue Checks,
(SUM(CASE WHEN s.ageofcheck < 60 THEN 1 ELSE 0 END))/Count(*) AS Number of Checks AS Percent All Other Checks
FROM
(
SELECT
c.checknumber,
DATEDIFF(dd,GETDATE(),c.checkdate) AS ageofcheck
FROM
checks_table AS c
) AS s

Sum values on date table where one column equals a selected value from another

I have a DimDate table that has a Billable Day Portion field that can be between 0 and 1. For each day that's in the current Bonus Period I want to multiple that Day Portion by 10, and then return the total sum.
To find out what Bonus Period we're in, I return ContinuousBonusPeriod where the date equals today:
Current Continuous Bonus Period:= CALCULATE(MAX(DimDate[ContinuousBonusPeriod]), FILTER(DimDate, DimDate[DateKey] = TODAY()))
I can see in the measure display this is correctly coming back as Bonus Period 1. However, when I then use ContinuousBonusPeriod in the measure to determine the number of days in the current period, it only returns 10, 1 day multiplied by the static 10.
Billable Hours This Period:= CALCULATE(SUMX(DimDate, DimDate[Billable Day Portion] * 10), FILTER(DimDate, DimDate[ContinuousBonusPeriod] = [Current Continuous Bonus Period]))
It appears to be only counting today's DimDate record instead of all the records whereContinuousBonusPeriod = 'Bonus Period 1' as I'd expect.
I needed to make sure no existing filter was applied to the DimDate table when calculating the Current Continuous Bonus Period:
Current Continuous Bonus Period:= CALCULATE(MAX(DimDate[ContinuousBonusPeriod]), FILTER(ALL(DimDate), DimDate[DateKey] = TODAY()))
(Notice the ALL() statement)

T-Sql business rule calculation

I have a complicated problem that needs to be solved in t-sql. Possibly without a cursor or a loop.
Given the following table with customer’s payment setup.
ID Frequency Amount Start Date End Date
1 Monthly 100 01-01-2016 N/A(ongoing)
The customer wants to know how much he\she will have to pay for the month of November.
If they were to close the account on 15 nov 2016.
For example:
Assume the customer wants to close their account on 15-nov-2016 and wants to know the $amount they will be paying from 1st of November to 15 of November.
Calculation
The frequency cycle for the customer payment is Monthly.
Taking the frequency into account we know that:
Customer start date of November will be 1st of November
The end date will be 30 November
Calculation formula
(DayUpToCloseDate/DaysInNov) * Amount = amount customer is asking.
DaysUpToCloseDate = 15 (diff 1st of nov and 15th of nov)
DaysInNov = 30
Amount = 100
(15/30)*100 = 50
So we can tell the customer he/she will be paying 50$ in November if they were to close the account on 15th of November.
First we need to declare the 2 variables, the date in question and the monthly amount due.
DECLARE #date datetime = '2017-11-15'
DECLARE #amountDue int = 100
Then to get the month to date amount due we can use:
SELECT CAST(DATEPART(DAY,#date) AS FLOAT)/DATEPART(DAY,DATEADD(MONTH,DATEDIFF(MONTH,0,#date)+1,0)-1) * #amountDue AS [MonthToDateAmountDue]
Here is how we got there.
SELECT
--This gets the first day of the next month and subtracts 1 day, getting the last day of the month the date falls in.
DATEADD(MONTH,DATEDIFF(MONTH,0,#date)+1,0)-1 AS [LastDayOfMonth]
--We now extract the day date part of the last day of the month to get the total days in the month.
,DATEPART(DAY,DATEADD(MONTH,DATEDIFF(MONTH,0,#date)+1,0)-1) AS [DaysInMonth]
--Finally, we get the day date part from our #date and divide by the total days of the month to get the percentage of the month we have completed.
--Since int does not do decimals, we use a float.
,CAST(DATEPART(DAY,#date) AS FLOAT)/DATEPART(DAY,DATEADD(MONTH,DATEDIFF(MONTH,0,#date)+1,0)-1) AS [PercentageOfMonthCompleted]
--And mulitply it by the amount due.
,CAST(DATEPART(DAY,#date) AS FLOAT)/DATEPART(DAY,DATEADD(MONTH,DATEDIFF(MONTH,0,#date)+1,0)-1) * #amountDue AS [MonthToDateAmountDue]
Edit: So, I just learned about the EOMONTH function. This can be shortened to
SELECT CAST(DATEPART(DAY,#date) AS FLOAT)/DATEPART(DAY,EOMONTH(#date)) * #amountDue AS AS [MonthToDateAmountDue]

SQL Statement to return previous quarter balance

I'm trying to create a query that can return the previous quarter balance for a series of records.
I have financial data for accountid (acctid), fiscal year (fyear), fiscal quarter (fquarter) and fiscal period (fperiod) that I'm summing and tracking through a series of other queries that I'm dropping in to a temporary table, that data includes the net change for the period (nperiod), net change for the quarter (nquarter) and net change for the year (nyear). Net change for the period is only the account transactions in the period, net change for the quarter is the cumulative total of the transactions that appear in periods 1-3, 4-6, 7-9 and 10-12 respectively (amount from previous periods are not calculated in proceeding quarters, ie the net change for the quarter resets to $0 for periods 4, 7 and 9) and the net change for the year is the total cumulative sum. I'm now trying to create a sql statement that returns the previous quarter end balance.
So for periods 1-3 I need to write a separate select statements to return the ending balance for the previous year, for periods 4-6 I want them ALL to return the net change for the quarter from period 3, for 7-9 I want to return the net change for the quarter from period 6 for all records and for period 10-12 I want to return net change for the quarter from period 9.
Can I get some assistance because I have a gigantic query that returns the max period per quarter, then the nquarter amount associated with that period and then trying to do a where exists, but something tells me there's a better way to do it.
Thanks!

SQL: Calculate the number of Days in a Month with no stock

I am trying to create a query than can calculate the number of days, in a given month, that a particular stock item was unavailable (ie: No. = 0).
Currently, I have developed a query that can calculate the number of days it has been from today's date where stock has been unavailable but what I am trying to actually calculate is, during a month, how many days was stock quantity = 0. ie: Month of Jan - on Jan 5, Jan 7 and Jan 20 there was no stock for Item A - this means that the number of days out of stock was = 3.
Extra Details:
Currently, I am basing my query in determining stock levels of the last transaction (ie: if, at the last transaction, the QTY of Stock = 0) then calculate the number of days between the transaction date and today.
Select [StockItems].StockCode,
Case When SUM([StockItems].Qty_On_Hand)=0 Then (Datediff(day, GETDATE(),MAX([Transactions].TransactionDate))) ELSE 0 END AS 'Days Out of Stock',
From dbo.[Transactions]
INNER JOIN [StockItems]
ON [Transactions].[AccountLink] = [StockItems].[StockLink]
Where [StockItems].StockCode LIKE '%XXX%'
AND [Transactions].TransactionDate>31/10/14
Group By [StockItems].StockCode
My Thoughts
There are different sorts of transactions - one of which is a good received transaction. Perhaps it is possible to calculate the days where Stock Qty was zero and a transaction occurred then count that date until goods were received.
Thoughts?
Thank You.
SELECT COUNT([StockItems].Qty_On_Hand
From dbo.[Transactions]
INNER JOIN [StockItems] ON [Transactions].[AccountLink] = [StockItems].[StockLink]
WHERE [StockItems].Qty_On_Hand)=0