(Forecasting) Calculating the balance in the future - sql

My product team has asked if I could create a very crude forecasting data table for them to work with. I have a pretty good idea on a lot of the steps I need to take, but I am stuck on figuring how to to calculate the Inventory Quantity for tomorrow, the next day, etc.
In my database, I am able to see our current quantity on hand. I would call that starting balance (for today). I am then going to create an average usage field and that will be my estimated daily sales. I will then take the starting balance - estimated daily sales = ending balance. I can do that for today, my question is how do I roll that formula forward for the next 120 days

You can use a recursive CTE to generate number from 0 to 120 and then calculate the day and balance with them.
DECLARE #estimated_daily_sales integer = 2;
DECLARE #starting_balance integer = 12345;
WITH
cte AS
(
SELECT 0 i
UNION ALL
SELECT i + 1 i
FROM cte
WHERE i + 1 <= 120
)
SELECT dateadd(day, i, convert(date, getdate())) day,
#starting_balance - i * #estimated_daily_sales balance
FROM cte
OPTION (MAXRECURSION 120);
db<>fiddle

Related

Automatically add rows based on schedule

I am currently building reports off a legacy system in SQL. The data is stored like the below.
Which I have now unpivoted, so this information is stored as a row per contract.
Here is what I am struggling to achieve, I am trying to build a payment schedule table for these contracts. The majority of them are fine as the "FirstPaymentDate" is the full payment we expect, however for some of them we receive the payment every 30 days until the contract is paid off.
So, in the example above, there is a contractvalue of 1000 and a sold term of 2 years. 1000/24 months = £41.6 per month. I need to display this as a table with each date that we expect payment up until the last date, such as the below
Not going to lie, I am struggling how to approach this. I have tried searching but not sure what labels I would search for to cover my issue.
Can anyone help with an approach to solve this? Even if it's just pointing me in the right direction?
You could do this with a recursive CTE. The code below outputs the desired result set you have shown. Your question asks for payments every 30 days but the desired result set example shows every month. This example shows a payment every month. If you need every 30 days you can update to DATEADD(DAY, 30, PaymentDate).
DROP TABLE IF EXISTS #Contract;
CREATE TABLE #Contract
(
ContractId INT
, SoldTerm INT
, FirstPaymentDate DATE
, ContractValue MONEY
, PaymentTerms INT
);
INSERT INTO #Contract VALUES (10222, 2, '01/04/2022', 1000, 30);
WITH ContractDates AS
(
SELECT ContractId, FirstPaymentDate AS PaymentDate, 1 AS Payment, (ContractValue / (SoldTerm * 12)) AS PaymentAmount, (SoldTerm * 12) AS LastPayment
FROM #Contract
UNION ALL
SELECT ContractId, DATEADD(MONTH, 1, PaymentDate), Payment + 1 AS Payment, PaymentAmount, LastPayment
FROM ContractDates
WHERE Payment + 1 <= LastPayment
)
SELECT * FROM ContractDates;
DROP TABLE IF EXISTS #Contract;

In SQL Server, how can I duplicate my current set of results for each month and increase them by 1.5*

EDIT: I updated this post cause I had to change my table, I had to change my Quantity from an INT to varchar to accommodate for decimals and now the below answers won't work, is varchar the best option for this?
Sorry, this might be a bit of a simple question for someone who knows what they're doing but I'm super new to SQL.
I've created this table (60 results in total)
However these are just the results for the first month of the year (January)
all further results are the same except the quantity is a 1.5% increase per month, so in February (2016-02-01) the first employee's six quantity's would be
111
165
463
156
99
63
However I'm having trouble getting my head around using scripts to enter results. I could manually enter each month but that would take me quite a few hours as there's 60 results for each month. So I was thinking maybe I could copy the current contents of the table into a temporary variable then create some sort of loop that has a 1.5% increase for each month and also changes the date to the relevant month, but I have absolutely no idea how to go about doing this.
If anyone could help me out with how I could go about doing this I'd be super grateful, I'm so stumped and I don't even know where to start
You can add the next month by doing:
insert into t(employee_id, month, product_id, quantity)
select employee_id, dateadd(month, 1, [month]), product_id, quantity*(1 + 0.015)
from t
where t.[month] = '2016-01-01';
I would recommend that you just repeat this 11 times, changing the where clause each time. You can set up a query to do all 11 months at the same time, but it is probably not worth the effort.
You can do an INSERT/SELECT where your table is both source and target:
INSERT INTO tab
SELECT Sale_ID, Employee_ID, dateadd(month, 1, [Month]), Product_ID, Quantity * 1.015
FROM tab
WHERE [Month] = '2016-01-01'
Repeat this for 10 times and increase the number of months by one in each repetiton.
To do the inserts for all month in a single query you could add a CROSS JOIN to a series of numbers from 1 to 11:
INSERT INTO tab
SELECT Sale_ID, Employee_ID, dateadd(month, numbers.n, [Month]), Product_ID, Quantity * 1.015
FROM tab CROSS JOIN (select sale_ID AS n from tab where sale_ID between 1 and 11) AS numbers
WHERE [Month] = '2016-01-01'

SQL - Finding largest period of activity by customer

I had a difficult time w/ the title, hope this is a little clearer...
I have a table of data (simplified) like so;
Date
Customer
Amount
1/1/2014
1
100.5
4/4/2014
1
122.5
2/1/2014
3
3.25
...but just short of a million records.
I would like to find the x day (let's say 90) period for each customer that has the largest total amount.
To phase the question a little differently, given all the transactions for all customers, for each customer I would like to find the 90 day period that has the largest total amount and what that total amount is in the period.
Trying to advise a brute force approach where I define all the possible ranges (or iterate over all possible ranges on the fly).
Any thoughts on a more elegant solution?
You can use a self-join for this, but the performance may not be so great:
select t.*,
(select sum(t2.amount)
from table t2
where t2.customer = t.customer and
t2.date >= dateadd(day, -90, t.date) and t2.date <= t.date
) as amount90
from table t;
There is a more efficient method in SQL Server 2012.
Here's a psuedo-code ish kind of answer that I think would work. It'd probably be really slow though.
You could have a function that calculates the number of activities provided a start date and number of days,
--function F
#userid, #startdate, #dayCount
SELECT COUNT(*)
FROM TABLE
WHERE
UserID = #userid
and date > #startDate
and date < Dateadd(#startdate, #dayCount)
and then do a max on that function?
select max(f(user, date))
from TableContainingDateRanges

Calculating run down dates using only percentages using Microsoft SQL Server

I have a table with information on resources, basics are:
ID Total Start End Used
----------------------------------------------
1 350 01-01-2012 31-12-2012 80.6%
2 250 01-01-2012 31-12-2012 51.5%
3 3500 01-01-2012 31-07-2013 12.5%
4 350 01-01-2012 31-10-2012 91.0%
Columns are:
Total -- Total number of the resource (Being currency, time, paper etc).
Start -- Start date of resource
End -- End date of resource
Used -- Percentage of the resource used to date
I have to try to work out (or estimate) when the resource will run out at the rate used so far.
I've tried several different ways using the percentage used and the percentage but nothing makes sense, I'm pretty sure there's a simple way to do this that's staring me in the face but I can't find it.
My ideal output would be in text below but I will probably format in the application:
You have used X% of your [resource name] in Y% of the time allotted,
at this rate the resource will run down around [Run Down Date].
Can anyone work out how this can be calculated?
SQL Fiddle to play with
Edit:
To try and make the problem clearer, I'll explain how I would calculate a single date:
For the first line (ID = 1).
Average % per day = Percentage (80.6) / Days between Start and Today (205)
Average % per day = 0.003931707%
% remaining = Percentage (80.6%)
% remaining = 19.4%
Days remaining = Average % per day (0.003931707%) / % remaining (19.4%)
Days remaining = 49.34243176
Project Run Down = Today + Days Remaining (49.34243176)
Project Run Down = 11/09/2012 (11th Sep)
I've tried converting this process into SQL but I can't get it to work.
you can try something like this
declare #res table(
id int identity(1,1)
,total int
,start date
,[end] date
,used float )
insert into #res(total, start , [end], used)
values
(350, '20120101', '20121231', 0.806)
,(250, '20120101', '20121231', 0.515)
select
*
,used/DATEDIFF(DAY,start,GETDATE()) as avUsePerDay
,1/(used/DATEDIFF(DAY,start,GETDATE())) as expectedDaysTotal
,DATEADD(day,1/(used/DATEDIFF(DAY,start,GETDATE())),start) as expectedToDie
from #res
That would be...
SELECT DATEDIFF(d, [StartDate], GETDATE()) * 100.0 / DATEDIFF(d, [StartDate], [EndDate]) AS PercentageTimeGone,
DATEADD(d, (100 - USED) / (Used / DATEDIFF(d, [StartDate], GETDATE())), GETDATE()) AS ProjectedEndingDate
FROM Resources
What's about
select id, total, start, end, datediff(day,start,getdate()) / ( total / 100 )
I am not good in calculating, but in numbers it's working.
all the best

calculating "Max Draw Down" in SQL

edit: it's worth reviewing the comments section of the first answer to get a clearer idea of the problem.
edit: I'm using SQLServer 2005
something similar to this was posted before but I don't think enough information was given by the poster to truly explain what max draw down is. All my definitions of max draw down come from (the first two pages of) this paper:
http://www.stat.columbia.edu/~vecer/maxdrawdown3.pdf
effectively, you have a few terms defined mathematically:
Running Maximum, Mt
Mt = maxu in [0,t] (Su)
where St is the price of a Stock, S, at time, t.
Drawdown, Dt
Dt = Mt - St
Max Draw Down, MDDt
MDDt = maxu in [0,t] (Du)
so, effectively what needs to be determined is the local maximums and minimums from a set of hi and low prices for a given stock over a period of time.
I have a historical quote table with the following (relevant) columns:
stockid int
day date
hi int --this is in pennies
low int --also in pennies
so for a given date range, you'll see the same stockid every day for that date range.
EDIT:
hi and low are high for the day and low for each day.
once the local max's and min's are determined, you can pair every max with every min that comes after it and calculate the difference. From that set, the maximum difference would be the "Max Draw Down".
The hard part though, is finding those max's and min's.
edit: it should be noted:
max drawdown is defined as the value of the hypothetical loss if the stock is bought at it's highest buy point and sold at it's lows sell point. A stock can't be sold at a minval that came before a maxval. so, if the global minval comes before the global maxval, those two values do not provide enough information to determine the max-drawdown.
Brutally inefficient, but very simple version using a view is below:
WITH DDView
AS (SELECT pd_curr.StockID,
pd_curr.Date,
pd_curr.Low_Price AS CurrPrice,
pd_prev.High_Price AS PrevPrice,
pd_curr.Low_Price / pd_prev.High_Price - 1.0 AS DD
FROM PriceData pd_curr
INNER JOIN PriceData pd_prev
ON pd_curr.StockID = pd_prev.StockID
AND pd_curr.Date >= pd_prev.Date
AND pd_curr.Low_Price <= pd_prev.High_Price
AND pd_prev.Date >= '2001-12-31' -- #param: min_date of analyzed period
WHERE pd_curr.Date <= '2010-09-31' -- #param: max_date of analyzed period
)
SELECT dd.StockID,
MIN(COALESCE(dd.DD, 0)) AS MaxDrawDown
FROM DDView dd
GROUP BY dd.StockID
As usually you would perform the analysis on specific time period, it would make sense to wrap the query in a stored procedure with the parameters #StartDate, #EndDate and possibly #StockID. Again, this is quite inefficient by design - O(N^2), but if you have good indices and not huge amount of data, SQL Server will handle it pretty good.
Some things we need to consider in the problem domain:
Stocks have a range of prices every day, often viewed in candlestick charts
lets call the highest price of a day HI
lets call the lowest price of a day LOW
the problem is constrained by time, even if the time constraints are the IPO date and Delisting Dates
the maximum drawdown is the most you could possibly lose on a stock over that timeframe
assuming a LONG strategy: logically if we are able to determine all local maxes (MAXES) and all local mins (MINS) we could define a set of where we pair each MAX with each subsequent MIN and calculate the difference DIFFS
Sometimes the difference will result in a negative number, however that is not a drawdown
therefore, we need to select append 0 in the set of diffs and select the max
The problem lies in defining the MAXES and the MINS, with the function of the curve we could apply calculus, bummer we can't. Obviously
the maxes need to come from the HI and
the MINS need to come from the LOW
One way to solve this is to define a cursor and brute force it. Functional languages have nice toolsets for solving this as well.
For SQL Server and for one stock at a time, try this:
Create Procedure 'MDDCalc'(
#StartDate date,
#EndDate date,
#Stock int)
AS
DECLARE #MinVal Int
DECLARE #MaxVal Int
DECLARE #MaxDate date
SET #MaxVal = (
SELECT MAX(hi)
FROM Table
WHERE Stockid = #Stock
AND Day BETWEEN (#Startdate-1) AND (#EndDate+1))
SET #MaxDate=(
SELECT Min(Date)
FROM Table
WHERE Stockid = #Stock
AND hi = #MaxVal)
SET #MinVal = (
SELECT MIN(low)
FROM Table
WHERE Stockid = #Stock
AND Day BETWEEN (#MaxDate-1) AND (#EndDate+1))
SELECT (#MaxVal-#MinVal) AS 'MDD'
I have encounter this problem recently, My solution is like this:
let data: 3,5,7,3,-1,3,-8,-3,0,10
add the sum one by one, if the sum is great than 0, set it 0, else get the sum, the result would be like this
0,0,0,0,-1,0,-8,-11,-11,-1
The Maximum draw down is the lowest value in the data, -11.
Is this what you're after?
select StockID,max(drawdown) maxdrawdown
from (
select h.StockID,h.day highdate,l.day lowdate,h.hi - l.lo drawdown
from mdd h
inner join mdd l on h.StockID = l.StockID
and h.day<l.day) x
group by StockID;
It's a SQL based brute force approach. It compares every low price after today's hi price within the same stock and finds the greatest difference between the two prices. This will be the Maximum Draw Down.
It doesn't compare consider the same day as possible for maximum draw down as we don't have enough info in the table to determine if the Hi price happened before the Lo price on the day.
Here is a SQL Server 2005 user-defined function that should return the correct answer for a single stockid very efficiently
CREATE FUNCTION dbo.StockMaxDD(#StockID int, #day datetime) RETURNS int AS
BEGIN
Declare #MaxVal int; Set #MaxVal = 0;
Declare #MaxDD int; Set #MaxDD = 0;
SELECT TOP(99999)
#MaxDD = CASE WHEN #MaxDD < (#MaxVal-low) THEN (#MaxVal-low) ELSE #MaxDD END,
#MaxVal = CASE WHEN hi > #MaxVal THEN hi ELSE #MaxVal END
FROM StockHiLo
WHERE stockid = #Stockid
AND [day] <= #day
ORDER BY [day] ASC
RETURN #MaxDD;
END
This would not, however, be very efficient for doing a number of stockids at the same time. If you need to do many/all of the stockids at once, then there is a similar, but substantially more difficult approach that can do that very efficiently.