Projected Payments based on Schedule table? - sql

I have a payment_schedule table:
CREATE TABLE [dbo].[scheduled_transaction](
[id] [int] IDENTITY(1,1) NOT NULL,
[description] [varchar](20) NOT NULL,
[account_id] [int] NOT NULL,
[account_transaction_type_id] [int] NOT NULL,
[third_party_id] [int] NOT NULL,
[first_payment_date] [date] NOT NULL,
[last_payment_date] [date] NULL,
[payment_frequency] [int] NOT NULL,
[payment_frequency_type_id] [int] NOT NULL,
[payment_amount] [decimal](18, 2) NOT NULL,
[notes] [varchar](100) NULL,
[deleted] [datetime] NULL,
[createuser] [int] NOT NULL,
[createdate] [datetime] NOT NULL,
[lastupdateuser] [int] NULL,
[lastupdatedate] [datetime] NULL) ON [PRIMARY]
)
This table holds scheduled bill payments for my home system. The frequency is simply Daily, weekly or monthly. So, payment frequency = 1, and payment frequency type = 3 (monthly) means that the payment is done every one month.
I also have a calendar table, which is a table of all dates between a large period (2000 and 2040). This is just a reference table that I think is useful for what I will be doing.
What I want to do now, is create a procedure that will return a table of dates from a given startdate to a given enddate, and for each date, return any payments that should be done on that date, based on my schedule table.
My plan is to create a temp table with all dates where payments will be due:
DECLARE #StartDate DATE
DECLARE #EndDate DATE
Set #StartDate = '01-JAN-2013'
SET #EndDate = '31-DEC-2013'
DECLARE #Schedule TABLE
(
ID INT NOT NULL IDENTITY(1,1),
TransactionDate DATE NOT NULL,
scheduled_transaction_id INT NOT NULL
)
Once that's populated, I can then use the calendar table and create a balance forecast.
But, getting the data into that table is tricky.
I think I need to go through each scheduled_transaction, and then, run through the calendar and see if a transaction would be done on that date? And then insert the row into my temp table?
So, then I think it would be nested cursors. For each scheduled_transaction row, then for each calendar row, and use some form of 'DATEADD' or something?
Could anyone assist me with this?

I'm assuming that you have #days table with a date for each day in calendar so 2013-01-01, 2013-01-02 etc.
Also I don't know when will you pay someone with monthly schedule and first payment date 2013-01-31...? So I will ignore such cases:
select d.d as [day], st.id as transactionId
from #days d
inner join [scheduled_transaction] st
on (d.d >= st.first_payment_date
and d.d <= st.last_payment_date
and (
(
st.payment_frequency = 2
and datediff(day, d.d, st.first_payment_date) % 7 = 0
)
or
(
st.payment_frequency = 1
)
or
(st.payment_frequency = 3
and day(st.first_payment_date) = day(d.d)
)
)
)

This can be done using common Table expressions. Execute the below code to list all payments due in the given date range.
DECLARE #StartDate DATE
DECLARE #EndDate DATE
Set #StartDate = '01-JAN-2013'
SET #EndDate = '31-DEC-2013'
;with schedule as
(
SELECT first_payment_date as Next_payment_date,* FROM scheduled_transaction
UNION ALL
SELECT DATEADD( dd,case when payment_frequency_type_id =1 then 1 -- Daily
else case when payment_frequency_type_id =2 then 7 --Weekly
else 30 end end --Monthly
,Next_payment_date) ,
id, description, account_id, account_transaction_type_id, third_party_id, first_payment_date, last_payment_date, payment_frequency, payment_frequency_type_id,
payment_amount, notes, deleted, createuser, createdate, lastupdateuser, lastupdatedate
FROM schedule
WHERE Next_payment_date < last_payment_date
)
SELECT * FROM SCHEDULE
WHERE Next_payment_date between #StarDate and #EndDate
This code assumes the payment frequency is stored in payment_frequency_type_id. If it is different make changes in the code accordingly.

Related

Create table with condition

I'm creating a table, which will have a date in one of two columns, or neither.
I'd like the third to auto populate with one of these values, without using an update.
CREATE TABLE [table1]
(
id [BIGINT] NOT NULL,
[date1] [DATETIME] NULL,
[date2] [DATETIME] NULL,
[date] AS (CASE
WHEN [date1] IS NOT NULL THEN [date1]
WHEN [date2] IS NOT NULL THEN [date2]
ELSE NULL
END)
)
This doesn't seem to work when I test using:
INSERT INTO [table1] (id, date1)
VALUES (1, GETDATE())
Can anyone help?
Your code appears to be fine, as seen in this db<>fiddle. However, I would suggest using COALESCE() instead of CASE:
CREATE TABLE [table1] (
id [bigint] NOT NULL,
[date1] [datetime] NULL,
[date2] [datetime] NULL,
[date] AS ( COALESCE(date1, date2) )
);
It is more concise.

Return zero for time slot if data is not present

I'm trying to get data for a period of two minutes every minute there should be data for each second for mid,sid,pid combination. if data is not present for a second for each combination it should return IC value as zero.For two minutes there will be 120 time slots if data is not present for mid,sid, pid combination for any time slot it should return zero.This data is used to plot line chart if data is not present it should go down to zero.
CREATE TABLE [dbo].[DeviceData](
[Id] [BIGINT] IDENTITY(1,1) NOT NULL,
[MId] [INT] NOT NULL,
[SId] [INT] NOT NULL,
[PId] [INT] NOT NULL,
[DataTime] [DATETIME] NOT NULL,
[IC] [INT] NOT NULL,
CONSTRAINT [PK_DeviceData] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
SELECT [MId] ,
[SId] ,
[PId] ,
[DataTime] ,
SUM([IC]) AS Value
FROM [DeviceData]
WHERE DataTime BETWEEN DATEADD(MINUTE, -2, GETUTCDATE())
AND GETUTCDATE()
GROUP BY [MId] ,
SID ,
PId ,
[DataTime];
You need a numbers CTE:
with Numbers as
(
select 1 as NN
union all
select NN+1
from Numbers
where NN < 120
)
, Times as
(
select dateadd(ss,
NN,
DATEADD(MINUTE,
-2,
dateadd(ms,
-datepart(ms,
GETUTCDATE()),
GETUTCDATE()) ) as Timeslot
from Numbers
)
select Timeslot, DD.*
from Times
left join DeviceData DD
on Timeslot = dateadd(ms, -datepart(ms, GETUTCDATE()),GETUTCDATE())
OPTION (MAXRECURSION 1000) -- This will bypass the recursion error

Update a column in table with information from another table

Im already have this:
USE [AdventureWorks2012];
--- somewhere create table
IF OBJECT_ID('[dbo].[PersonPhone]','U') IS NOT NULL
DROP TABLE [dbo].[PersonPhone]
CREATE TABLE [dbo].[PersonPhone](
[BusinessEntityID] [int] NOT NULL,
[PhoneNumber] nvarchar(25) NOT NULL,
[PhoneNumberTypeID] [int] NOT NULL,
[ModifiedDate] [datetime] NOT NULL)
--- and append new column
ALTER Table [dbo].[PersonPhone]
ADD [StartDate] date NULL
--- after this, i want copy dates in this column (at another table, we increase dates by one)
UPDATE [dbo].[PersonPhone]
SET [StartDate] = [NewTable].[NewDate]
FROM (
SELECT DATEADD(day, 1, [HumanResources].[EmployeeDepartmentHistory].[StartDate]) AS [NewDate],
row_number() over(order by (select 1)) AS [RN]
FROM [HumanResources].[EmployeeDepartmentHistory]
) [NewTable]
How to improve query to copy the values ​​from [NewTable] to [dbo].[PersonPhone].[StartDate] row?
At your Update:
UPDATE [dbo].[PersonPhone]
SET [StartDate] = DATEADD(day, 1, [HumanResources].[EmployeeDepartmentHistory].[StartDate])
FROM [HumanResources].[EmployeeDepartmentHistory]
GO
SELECT row_number() over(order by (select 1)) AS [RN] FROM [HumanResources].[EmployeeDepartmentHistory]

what's the right way of joning two tables, group by a column, and select only one row for each record?

I have a crews table
CREATE TABLE crew(crew_id INT, crew_name nvarchar(20), )
And a time log table, which is just a very long list of actions performed by the crew
CREATE TABLE [dbo].[TimeLog](
[time_log_id] [int] IDENTITY(1,1) NOT NULL,
[experiment_id] [int] NOT NULL,
[crew_id] [int] NOT NULL,
[starting] [bit] NULL,
[ending] [bit] NULL,
[exception] [nchar](10) NULL,
[sim_time] [time](7) NULL,
[duration] [int] NULL,
[real_time] [datetime] NOT NULL )
I want to have a view that shows only one row for each crew with the latest sim_time + duration .
Is a view the way to go? If yes, how do I write it? If not, what's the best way of doing this?
Thanks
Here is a query to select what you want:
select * from (
select
*,
row_number() over (partition by c.crew_id order by l.sim_time desc) as rNum
from crew as c
inner join TileLog as l (on c.crew_id = l.crew_id)
) as t
where rNum = 1
it depends on what you need that data for.
anyway, a simple query to find latest sim time would be something like
select C.*, TL.sim_time
from crew C /*left? right? inner?*/ join TimeLog TL on TL.crew_id = C.crew.id
where TL.sim_time in (select max(timelog_subquery.sim_time) from TimeLog timelog_subquery where crew_id = C.crew_id )

Problem with SQL In Line Table Function... Query

Hi I have the following query running a function to get the Standard Deviation for a set of Tickers in the following table...
GO
CREATE TABLE [dbo].[Tickers](
[ticker] [varchar](10) NULL,
[date] [datetime] NULL,
[high] [float] NULL,
[low] [float] NULL,
[open] [float] NULL,
[close] [float] NULL,
[volume] [float] NULL,
[time] [datetime] NULL,
[change] [float] NULL
) ON [PRIMARY]
THE PROBLEM: THE FOLLOWING IN LINE TABLE FUNCTION RETURNS STDDEV CALC which is in turn used by a SPROC To calculate Bollinger bands ... (mov average + 2 * STDEV) etc...
The results that I get for some Tickers has weird data ... this is the result set for the ticker 'ATE' or just a sample of the result set.
dayno ticker stddev
484 11/13/2009 0.544772123613694
485 11/16/2009 0.323959874058724
486 11/17/2009 0.287909129182731
487 11/18/2009 0.225018517756637
488 11/19/2009 4.94974746848848E-02
489 11/20/2009 4.94974746848848E-02
As you can see the last two values for some of the tickers results in 'weird data' and the actual ticker table is within very normal ranges.
As you can see from the following in line table function there was some funny stuff going on at the end because it is using a 20 day period and the last value always came back as NULL, so I asked experts to adjust and this is what Peter came up with... it usually works find but as you can see above sometimes does not - does anyone have a suggestion on how I may fix this dilemma??
ALTER FUNCTION dbo.GetStdDev3 (#TKR VARCHAR(10))
RETURNS #results TABLE (
dayno SMALLINT IDENTITY(1,1) PRIMARY KEY
, [date] DATETIME
, [stddev] FLOAT
)
AS BEGIN
DECLARE #min_sysdate DATETIME, #min_tkrdate DATETIME, #rowcount SMALLINT
SET #min_sysdate = DATEADD(DAY,-731,GETDATE())
SET #min_tkrdate = DATEADD(DAY,20,(
SELECT MIN(DATE) FROM TICKERS WHERE TICKER = #TKR))
INSERT #results ([date],[stddev])
SELECT x.[date], ISNULL(STDEV(y.[Close]),0) AS stdev
FROM Tickers x
JOIN Tickers y ON x.[DATE] BETWEEN DATEADD(DAY,-20,y.[DATE]) AND y.[DATE]
WHERE x.[DATE] > #min_tkrdate
AND x.[DATE] > #min_sysdate
AND x.TICKER = #TKR
AND y.TICKER = #TKR
GROUP BY x.[DATE]
SET #rowcount = ##ROWCOUNT
UPDATE #results SET [stddev] = (
SELECT [stddev] FROM #results WHERE dayno = #rowcount-1)
WHERE dayno = #rowcount
RETURN
4.94974746848848E-02 is actually the same thing as 0.0494974746848848
Are you sure this is in error? Seems to me it could just be a low deviation.
Ditto what "d." said. The standard deviation for the last two dates is low, but it was decreasing over time anyway. Also, all that the last update statement does is to set the last row (latest date) in the set equal to the second-to-last value. (Perhaps "adjsut" might have been "delete"?)