August 23, 2017 at 8:19 pm
I am not sure if this can be done but I would like to create a view (or 2nd option a new table that gets updated each night).
I have a table that is updated each night from a completely different database and I then want to create a report that shows the total Investments falling due each month based on the assumption that the investment will be renewed for the same term and the same amount.
My logic is to create a view (or table) that will check if the maturity date is less than the current date plus 1,100 (being the approx. number of days in the next 3 years as the maximum term of the investment is 3 years). If the maturity date is less than the current date plus 1100 days it would be added but also the script would then add another record with the lodgement date being the maturity date of the original investment and the maturity date of this new record being the new lodgement date plus the term in days. The script would than check if that maturity date is less than the current date plus 1100 days. If not add another record with the lodgement date being the maturity date of the 2nd record for that certificate and so on.
I imagine that I would use a “Maximum” logic but not sure where to start.
Example:
Current Table View or New Table Result
Cert No | Lodge Date | Mat Date | Term | Amount | Cert No | Lodge Date | Mat Date | Term | Amount |
1 | 11/02/2017 | 11/02/2018 | 365 | $ 5,000 | 1 | 11/02/2017 | 11/02/2018 | 365 | $ 5,000 |
2 | 15/06/2017 | 14/09/2017 | 91 | $ 6,500 | 2 | 15/06/2017 | 14/09/2017 | 91 | $ 6,500 |
3 | 30/06/2017 | 29/09/2017 | 91 | $ 7,500 | 3 | 30/06/2017 | 29/09/2017 | 91 | $ 7,500 |
4 | 16/07/2017 | 14/09/2017 | 60 | $ 15,000 | 4 | 16/07/2017 | 14/09/2017 | 60 | $ 15,000 |
5 | 1/08/2017 | 1/09/2017 | 31 | $ 1,500 | 5 | 1/08/2017 | 1/09/2017 | 31 | $ 1,500 |
5 | 1/09/2017 | 2/10/2017 | 31 | $ 1,500 | |||||
4 | 14/09/2017 | 13/11/2017 | 60 | $ 15,000 | |||||
2 | 14/09/2017 | 14/12/2017 | 91 | $ 6,500 | |||||
3 | 29/09/2017 | 29/12/2017 | 91 | $ 7,500 | |||||
5 | 2/10/2017 | 2/11/2017 | 31 | $ 1,500 | |||||
5 | 2/11/2017 | 3/12/2017 | 31 | $ 1,500 | |||||
4 | 13/11/2017 | 12/01/2018 | 60 | $ 15,000 | |||||
5 | 3/12/2017 | 3/01/2018 | 31 | $ 1,500 | |||||
2 | 14/12/2017 | 15/03/2018 | 91 | $ 6,500 | |||||
3 | 29/12/2017 | 30/03/2018 | 91 | $ 7,500 |
So my questions are
1. Can I create a view that has MORE records than the table that I am extracting data from?
2. Any suggestions on how to get the system to cycle through the highest maturity date and add another record (if applicable) before going on to the next record from the original table? Any web sites or links that might direct me in the right direction?
Once I have this view or new table I can do the report but just cannot seem to work out how to get the view or new report. A view is the preferred option as it updates automatically as the original table gets updated each night.
Many thanks 🙂
August 24, 2017 at 1:34 am
les.61 - Wednesday, August 23, 2017 8:19 PM1. Can I create a view that has MORE records than the table that I am extracting data from?
2. Any suggestions on how to get the system to cycle through the highest maturity date and add another record (if applicable) before going on to the next record from the original table? Any web sites or links that might direct me in the right direction?
1. Yes. A view is just a predefined SELECT statement, and doesn't have to select from one table, or could return records multiple times from the same one.
2. I'm not entirely sure I understand your question, in all honesty. Someone else might, but It would be good if you could outline your logic in a step by step process for an individual record, it'll definitely help the understanding. Also, please ensure you provide your data in a consumable format. Can you post DDL and DLM please? Have a look at the link in my signature on how to do this.
Thom~
Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
Larnu.uk
August 24, 2017 at 6:41 am
Like Thom said already, it is quite possible to create such a view.
To select such a result I suggest to use a tally date table. Such a table contains basically all dates between a certain range (like from 2000-1-1 till 2030-1-1). If you combine this tally table with an OUTER JOIN to your base table, it is rather easy to add the missing timerange from your base table to your final result. From your sample it looks like the column [Term] contains the number of days between [Lodge Date] and [Mat Date]. I cant read in your description how to determine the values for column [Mat Date] and [Amount] for those new rows. You can use the query below as base to start building your end solution:. The COALESCE is used to display the value from the row from the base table when available or show an alternative when the row from the base table does not exists.
SELECT COALESCE(base.[Lodge date], tally.[date]) as 'Lodge Date'
, COALESCE(base.[Mat Date], dateadd(day, 1, tally.[date])) as 'Mat Date'
, COALESCE(base.[Term], 1) as 'Term'
, COALESCE(base.[Amount], '$ 50') as 'Amount'
from [your_table] base
right outer join [tally_date] tally
on base.[Lodge Date] = tally.[date]
where tally.[date] > '20170101'
AND tally.[date] < dateadd(day, 1100, getdate())
August 24, 2017 at 7:36 am
Using a Tally table of integers
SET DATEFORMAT DMY;
WITH cte (CertNo,LodgeDate,MatDate,Term,Amount) AS (
SELECT CertNo,LodgeDate,MatDate,Term,Amount
FROM (VALUES (1,CAST('11/02/2017' as date),CAST('11/02/2018' as date),365,5000),
(2,CAST('15/06/2017' as date),CAST('14/09/2017' as date),91,6500),
(3,CAST('30/06/2017' as date),CAST('29/09/2017' as date),91,7500),
(4,CAST('16/07/2017' as date),CAST('14/09/2017' as date),60,15000),
(5,CAST('01/08/2017' as date),CAST('01/09/2017' as date),31,1500)
) a (CertNo,LodgeDate,MatDate,Term,Amount)
)
SELECT CertNo,LodgeDate,MatDate,Term,Amount FROM cte
UNION ALL
SELECT CertNo,DATEADD(day,Term*(N-1),MatDate),DATEADD(day,Term*N,MatDate),Term,Amount
FROM cte
JOIN dbo.Tally ON N BETWEEN 1 AND 35
WHERE DATEADD(day,Term*N,MatDate) < DATEADD(day,1100,GETDATE());
Far away is close at hand in the images of elsewhere.
Anon.
August 24, 2017 at 9:03 pm
I have looked at all the responses and still having trouble trying to get it to work. I hope I can explain it a bit better.
The table is created and data added as follows:
IF OBJECT_ID('MaturityTest1','U') IS NOT NULL
DROP TABLE MaturityTest1
/****** Object: Table [dbo].[MaturityTest] Script Date: 25/08/2017 12:09:25 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MaturityTest1](
[CertNo] [float] NULL,
[LodgeDate] [datetime] NULL,
[MatDate] [datetime] NULL,
[TermDays] [float] NULL,
[Amount] [money] NULL
) ON [PRIMARY]
GO
--===== All Inserts into the IDENTITY column
--SET IDENTITY_INSERT MaturityTest1 ON
--===== Insert the test data into the test table
INSERT INTO [dbo].MaturityTest1
(CertNo, LodgeDate, MatDate, TermDays, Amount)
SELECT '1','Feb 11 2017','Feb 11 2018','365','5000' UNION ALL
SELECT '2','Jun 15 2017','Sep 14 2017','91','6500' UNION ALL
SELECT '3','Jun 30 2017','Sep 29 2017','91','7500' UNION ALL
SELECT '4','Jul 16 2017','Sep 14 2017','60','15000' UNION ALL
SELECT '5','Aug 01 2017','Sep 01 2017','31','1500'
--===== Set the identity insert back to normal
--SET IDENTITY_INSERT MaturityTest1 ON
I also created a 2nd table as follows:
USE [LesTest]
GO
/****** Object: Table [dbo].[Dates2100] Script Date: 25/08/2017 12:35:12 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Dates2100a](
[Date] [datetime] NULL
) ON [PRIMARY]
GO
I filled this with every date from the following (I did not know how to insert this straight into the table so copied to Excel and imported to Table)
WITH Dates AS (
SELECT
[Date] = CONVERT(DATETIME,'01/01/2017')
UNION ALL SELECT
[Date] = DATEADD(DAY, 1, [Date])
FROM
Dates
WHERE
Date < '01/01/2106'
) SELECT
[Date]
FROM
Dates
OPTION (MAXRECURSION 32765)
I then created the following script
select CertNo, LodgeDate, MatDate, TermDays, Amount
,CertNo,DATEADD(day,TermDays,LodgeDate) as Lodgedate,DATEADD(day,TermDays,MatDate) as MatDate,TermDays,Amount
from MaturityTest
left outer join [dbo].[Dates2100]
on lodgedate = date
where LodgeDate < dateadd(day, 1100, getdate()) and certno like '1'
The certno like '1' was just to simplify the end result for testing and explanation
The result was
CertNo LodgeDate MatDate TermDays Amount CertNo Lodgedate MatDate TermDays Amount
1 2017-02-11 00:00:00.000 2018-02-11 00:00:00.000 365 5000.00 1 2018-02-11 00:00:00.000 2019-02-11 00:00:00.000 365 5000.00
However I want the result to look like
CertNo | LodgeDate | MatDate | TermDays | Amount |
1 | 2017-02-11 00:00:00.000 | 2018-02-11 00:00:00.000 | 365 | 5000.00 |
1 | 2018-02-11 00:00:00.000 | 2019-02-11 00:00:00.000 | 365 | 5000.00 |
1 | 2019-02-11 00:00:00.000 | 2020-02-11 00:00:00.000 | 365 | 5000.00 |
1 | 2020-02-11 00:00:00.000 | 2021-02-10 00:00:00.000 | 365 | 5000.00 |
As you can see what I am trying to create is a list of the current LodgeDate, MatDay, Term and Amount plus any future dates (assuming the term and amount stays the same) at will occur until the Lodgement Date is greater than the current date plus 1,100 days.
Any assistance in pointing me in the right direction is appreciated.
JeffM is this the DDL and DLM that you talked about? Sorry I could not do the insert into Dates2100 but just no idea how to do it.
Many thanks
August 25, 2017 at 4:51 am
I've completed your query sample to include the filling of the tally table.
From your sample I notice a few mistakes. First you need a RIGHT outer join instead of a LEFT, because the tally table contains all values of the date column you need. Second you don't have any logic in place on how to handle non matching rows. Just showing the logic as additional columns doesn't handle the non-matching rows. You'll need to add them inside a COALESCE function.
I have taken your sample, added the filling of the tally and at the bottom I've added two queries. The first one displays all selected rows from the tally table and only includes the matching rows from the MaturityTest1 table. When no match is found, the values remain a NULL. I think the second SELECT is more to your desired solution. This one has put the logic inside the COALESCE, so every row contains non-NULL values. Keep in mind the non-matching rows also doesn't have values for "TermDays" and "Amount", so using these columns inside the replacing logic doesn't make any sense. That's why I have used fixed values instead.
IF OBJECT_ID('MaturityTest1','U') IS NOT NULL
DROP TABLE MaturityTest1
/****** Object: Table [dbo].[MaturityTest] Script Date: 25/08/2017 12:09:25 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [MaturityTest1](
[CertNo] [float] NULL,
[LodgeDate] [datetime] NULL,
[MatDate] [datetime] NULL,
[TermDays] [float] NULL,
[Amount] [money] NULL
) ON [PRIMARY]
GO
--===== All Inserts into the IDENTITY column
--SET IDENTITY_INSERT MaturityTest1 ON
--===== Insert the test data into the test table
INSERT INTO MaturityTest1
(CertNo, LodgeDate, MatDate, TermDays, Amount)
SELECT '1','Feb 11 2017','Feb 11 2018','365','5000' UNION ALL
SELECT '2','Jun 15 2017','Sep 14 2017','91','6500' UNION ALL
SELECT '3','Jun 30 2017','Sep 29 2017','91','7500' UNION ALL
SELECT '4','Jul 16 2017','Sep 14 2017','60','15000' UNION ALL
SELECT '5','Aug 01 2017','Sep 01 2017','31','1500'
-- create a date table and fill it with every day from the year 2017 ÷ 2020
IF OBJECT_ID('Tally_Date') IS NOT NULL
DROP TABLE Tally_Date;
CREATE TABLE Tally_Date (
[Date] datetime2(0)
);
INSERT INTO Tally_Date
SELECT DATEADD(day, Num - 1, '2017-01-01T00:00:00')
FROM (
SELECT TOP 1461 ROW_NUMBER() OVER(ORDER BY i1.TABLE_CATALOG) AS Num
FROM master.INFORMATION_SCHEMA.COLUMNS i1
CROSS JOIN master.INFORMATION_SCHEMA.COLUMNS i2
) sub;
-- query below shows all dates (taken from the TALLY table) and includes values from matching rows from table #MaturityTest1.
-- The values remain NULL if there is no matching row.
select date, CertNo, LodgeDate, MatDate, TermDays, Amount,CertNo,DATEADD(day,TermDays,LodgeDate) as Lodgedate,DATEADD(day,TermDays,MatDate) as MatDate,TermDays,Amount
from MaturityTest1
right outer join Tally_Date
on lodgedate = date
where date < dateadd(day, 1100, getdate()) --and certno like '1'
order by date;
-- query below shows all dates (taken from the TALLY table) and includes values from matching rows from table #MaturityTest1.
-- The NULL values are replaced within the COALESCE function to a derived value (DATEADD) or a fixed value.
SELECT COALESCE(base.[Lodgedate], tally.[date]) as 'Lodge Date'
, COALESCE(base.[MatDate], dateadd(day, 1, tally.[date])) as 'Mat Date'
, COALESCE(base.[TermDays], 1) as 'Term'
, COALESCE(base.[Amount], '$ 50') as 'Amount'
from [MaturityTest1] base
right outer join [tally_date] tally
on base.[LodgeDate] = tally.[date]
where tally.[date] > '20170101'
AND tally.[date] < dateadd(day, 1100, getdate())
order by tally.Date;
August 25, 2017 at 4:58 am
Btw: if you need to fill non-matching rows using information from other (previous) rows, take a look at the LEAD and LAG functions. This article on DatabaseJournal.com has a nice explanation and clear example.
Viewing 7 posts - 1 through 6 (of 6 total)
You must be logged in to reply to this topic. Login to reply