按季度划分的最新可用持股

Latest Available Holdings by Quarter

我希望修复这个不使用游标的问题(除非必要)。

我们每季度都会收到投资部门提交的持股报告。它们按参考日期、投资、持有和货币类型加载,使每一行都不同。此数据放在 MSFT SQL 服务器的数据库中。有时,一项投资不会提交该季度的持股。在这种情况下,我们希望提取最新的可用季度持股量(可能是 3 或 4 个季度前),这样在对多个参考日期进行趋势分析时就不会出现差距。

除了差距之外,我们还需要将上次提交的数据提前到整个table的MAX([Reference Date])。对于以下,此日期为 6/30/2021。

我们有一个日期维度 table。对于提交的提交,[数据可用性] 列应显示 'Include Latest from Previous Submissions'.

第一个 DDL 中显示了问题(第二个 DDL 示例中显示了修复):

问题:

修复:

修复前的示例数据:

CREATE TABLE dbo.TestInvestments
    ( [Reference Date] DATE, [Investment] [nvarchar](1000) NOT NULL, [holding] [nvarchar](1000) NOT NULL, [Currency Type] [varchar](100) NULL, [Data Availability] varchar(22), [Current Value] [float] NULL, [Current Cost] [float] NULL, [Realized] [Float] NULL)
;
    
INSERT INTO TestInvestments
    ( [Reference Date], [Investment], [holding], [Currency Type], [Data Availability], [Current Value], [Current Cost], [Realized])
VALUES

    ('2019-12-31', 'Investment 1', 'Quality Care', 'LOCAL', 'Current Qtr Submission', 0, 0, 15757000),
    ('2019-12-31', 'Investment 1', 'Quality Care', 'USD', 'Current Qtr Submission', 0, 0, 15757000),
    ('2019-12-31', 'Investment 1', 'TransUnion', 'LOCAL', 'Current Qtr Submission', 0, 0, 631410000),
    ('2019-12-31', 'Investment 1', 'TransUnion', 'USD', 'Current Qtr Submission', 0, 0, 631410000),
    ('2020-03-31', 'Investment 1', 'Quality Care', 'LOCAL', 'Current Qtr Submission', 0, 0, 15757000),
    ('2020-03-31', 'Investment 1', 'Quality Care', 'USD', 'Current Qtr Submission', 0, 0, 15757000),
    ('2020-12-31', 'Investment 2', 'West Corp.', 'LOCAL', 'Current Qtr Submission', 63872528, 54756087, 0),
    ('2020-12-31', 'Investment 2', 'West Corp.', 'USD', 'Current Qtr Submission', 63872528, 54756087, 0),
    --End of Addition
    ('2021-06-30', 'Investment 3', 'Tinkers', 'USD', 'Current Qtr Submission', 12536, 26541, 0)
;

修复后的预期结果如下所示:

CREATE TABLE dbo.TestInvestmentsFixed
    ( [Reference Date] DATE, [Investment] [nvarchar](1000) NOT NULL, [holding] [nvarchar](1000) NOT NULL, [Currency Type] [varchar](100) NULL, [Data Availability] varchar(100), [Current Value] [float] NULL, [Current Cost] [float] NULL, [Realized] [Float] NULL)
;
    
INSERT INTO TestInvestmentsFixed
    ( [Reference Date], [Investment], [holding], [Currency Type], [Data Availability], [Current Value], [Current Cost], [Realized])
VALUES

    ('2019-12-31', 'Investment 1', 'Quality Care', 'LOCAL', 'Current Qtr Submission', 0, 0, 15757000),
    ('2019-12-31', 'Investment 1', 'Quality Care', 'USD', 'Current Qtr Submission', 0, 0, 15757000),
    ('2019-12-31', 'Investment 1', 'TransUnion', 'LOCAL', 'Current Qtr Submission', 0, 0, 631410000),
    ('2019-12-31', 'Investment 1', 'TransUnion', 'USD', 'Current Qtr Submission', 0, 0, 631410000),
    ('2020-03-31', 'Investment 1', 'Quality Care', 'LOCAL', 'Current Qtr Submission', 0, 0, 15757000),
    ('2020-03-31', 'Investment 1', 'Quality Care', 'USD', 'Current Qtr Submission', 0, 0, 15757000),
    --Data added here.  TransUnion not brought forward from 12/31/19
    ('2020-06-30', 'Investment 1', 'Quality Care', 'LOCAL', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    ('2020-06-30', 'Investment 1', 'Quality Care', 'USD', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    ('2020-09-30', 'Investment 1', 'Quality Care', 'LOCAL', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    ('2020-09-30', 'Investment 1', 'Quality Care', 'USD', 'Include Latest from Previous Submissions', 0, 0, 15757000),    
    ('2020-12-31', 'Investment 1', 'Quality Care', 'LOCAL', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    ('2020-12-31', 'Investment 1', 'Quality Care', 'USD', 'Include Latest from Previous Submissions', 0, 0, 15757000),        
    ('2021-03-31', 'Investment 1', 'Quality Care', 'LOCAL', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    ('2021-03-31', 'Investment 1', 'Quality Care', 'USD', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    ('2021-06-30', 'Investment 1', 'Quality Care', 'LOCAL', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    ('2021-06-30', 'Investment 1', 'Quality Care', 'USD', 'Include Latest from Previous Submissions', 0, 0, 15757000),
    --End of Addition
    ('2020-12-31', 'Investment 2', 'West Corp.', 'LOCAL', 'Current Qtr Submission', 63872528, 54756087, 0),
    ('2020-12-31', 'Investment 2', 'West Corp.', 'USD', 'Current Qtr Submission', 63872528, 54756087, 0),
    --This data is added
    ('2021-03-31', 'Investment 2', 'West Corp.', 'LOCAL', 'Include Latest from Previous Submissions', 63872528, 54756087, 0), 
    ('2021-03-31', 'Investment 2', 'West Corp.', 'USD', 'Include Latest from Previous Submissions', 63872528, 54756087, 0),
    ('2021-06-30', 'Investment 2', 'West Corp.', 'LOCAL', 'Include Latest from Previous Submissions', 63872528, 54756087, 0),     
    ('2021-06-30', 'Investment 2', 'West Corp.', 'USD', 'Include Latest from Previous Submissions', 63872528, 54756087, 0),
    --End of Addition
    ('2021-06-30', 'Investment 3', 'Tinkers', 'USD', 'Current Qtr Submission', 12536, 26541, 0)
;

当前解决方案(不是 working/and 是光标):我认为这太过分了,不能不在 fiddle 中,但我重新创建了这个来自我目前使用的解决方案。这带来了不应该因为像 TransUnion 这样的持股而带来的持股。我将介绍的这个重新创建的脚本似乎还有一些其他问题:

/*                       *******************     USD Only in this section - below USD is the Local       *******************************   */

IF OBJECT_ID('TEMPDB.dbo.#TestInvestments_USD_Only') IS NOT NULL DROP TABLE #TestInvestments_USD_Only
IF OBJECT_ID('TEMPDB.dbo.#nums') IS NOT NULL DROP TABLE #nums
IF OBJECT_ID('TEMPDB.dbo.#TestInvestments_LatestAvailable_USD') IS NOT NULL DROP TABLE #TestInvestments_LatestAvailable_USD

/*create temp table for USD Holdings only - Local Currency is unioned later*/
Select * into #TestInvestments_USD_Only from TestInvestments where [Currency Type] = 'USD' 

/* Table to contain the missing holdings*/
CREATE TABLE #TestInvestments_LatestAvailable_USD(
   [Reference Date] date  NOT NULL
  ,Investment nvarchar (200) NOT NULL
  ,[Holding Key] int NOT NULL
  ,Holding nvarchar (200) NOT NULL
  ,[Currency Type] [varchar](100) NULL
  ,[Data Availability] varchar(100) NULL
  ,[Current Value] [float] NULL
  ,[Current Cost] [float] NULL
  ,[Realized] [float] NULL
  ,primary key(Investment, [Holding Key], [Reference Date])
);

--cartesian join to create a table of row numbers (65,536 rows)
create table #nums(n integer not null primary key);
with
  p1 as (select 0 as n union all select 0), -- 2
  p2 as (select 0 as n from p1 cross join p1 as b), -- 4
  p3 as (select 0 as n from p2 cross join p2 as b), -- 16
  p4 as (select 0 as n from p3 cross join p3 as b), -- 256
  p5 as (select 0 as n from p4 cross join p4 as b) -- 65536
insert into #nums(n) select row_number() over(order by (select 0)) from p5;

--Create Variables for all of the USD columns to store the missing rows
declare
@prev_Investment nvarchar(200) ,@Investment nvarchar(200),
@prev_HoldingKey int, @HoldingKey int,
@prev_ReferenceDate date ,@ReferenceDate date,
@prev_Holding nvarchar(200) ,@Holding nvarchar(200),
@prev_CurrencyType varchar(100) ,@CurrencyType varchar(100),
@prev_DataAvailability varchar(100),@DataAvailability varchar(100),
@prev_CurrentValue float ,@CurrentValue float,
@prev_CurrentCost float ,@CurrentCost float,
@prev_Realized float ,@Realized float,
@qdiff integer;

--Begin loop for USD to look for missing rows based on Investment, holdingkey and reference date, and add missing data to a variables

declare
  c cursor forward_only static read_only for
    select Investment, [Holding Key], [Reference Date],Holding,[Currency Type],[Data Availability],[Current Value],[Current Cost],[Realized]
     from #TestInvestments_USD_Only
    union all
    select Investment, [Holding Key], ref_date, NULL,   NULL,   NULL,   NULL,  NULL,  NULL
    from (select dateadd(q,1,max([reference date])) as ref_date from #TestInvestments_USD_Only) as a
    cross join (select distinct Investment from #TestInvestments_USD_Only) as b
    cross join (select distinct [Holding Key] from #TestInvestments_USD_Only) as c
    --cross join (select distinct [Currency Type] from #TestInvestments_USD_Only) as d
    order by Investment, [Holding Key], [Reference Date], [Current Value] desc;
open c;

--Set the most recent data = to the reference date we are currently looking at (if missing data, then set the latest data = current quarter in the loop)
fetch next from c into @Investment,@HoldingKey,@ReferenceDate,@Holding,@CurrencyType,@DataAvailability,@CurrentValue,@CurrentCost,@Realized;  
select
    @prev_Investment=@Investment,
    @prev_HoldingKey=@HoldingKey,
    @prev_ReferenceDate=@ReferenceDate,
    @prev_Holding=@Holding,
    @prev_CurrencyType=@CurrencyType,
    @prev_DataAvailability=@DataAvailability,
    @prev_CurrentValue=@CurrentValue,
    @prev_CurrentCost=@CurrentCost,
    @prev_Realized=@Realized
;


--Insert the missing data into the temp table
while @@fetch_status = 0 begin
  fetch next from c into @Investment,@HoldingKey,@ReferenceDate,@Holding,@CurrencyType,@DataAvailability,@CurrentValue,@CurrentCost,@Realized;
  set @qdiff = datediff(q, @prev_ReferenceDate, @ReferenceDate);
  if @prev_Investment = @Investment and
     @prev_HoldingKey = @HoldingKey and
     @qdiff > 1
  begin
    insert into #TestInvestments_LatestAvailable_USD([Investment],[Holding Key],[Reference Date],[Holding],[Currency Type],[Data Availability],[Current Value],[Current Cost],Realized)
  select
        @prev_Investment,@prev_HoldingKey,eomonth(dateadd(q, #nums.n, @prev_ReferenceDate)),@prev_Holding,@prev_CurrencyType,@prev_DataAvailability,@prev_CurrentValue,@prev_CurrentCost,@prev_Realized
      from #nums
      where #nums.n < @qdiff + iif(@CurrentValue = -1, 1, 0);
  end;
  select
    @prev_Investment=@Investment,
    @prev_ReferenceDate=@ReferenceDate,
    @prev_HoldingKey=@HoldingKey,
    @prev_Holding=@Holding,
    @prev_CurrencyType=@CurrencyType,
    @prev_DataAvailability=@DataAvailability,
    @prev_CurrentValue=@CurrentValue,
    @prev_CurrentCost=@CurrentCost,
    @prev_Realized=@Realized
        
END
CLOSE c
DEALLOCATE c

--//////////////////////////////////////////////////////////////////////////////////////////////////////

/*                       *******************     Local Currency Only in this section - below USD is the Local       *******************************   */

IF OBJECT_ID('TEMPDB.dbo.#TestInvestments_Local_Only') IS NOT NULL DROP TABLE #TestInvestments_Local_Only
IF OBJECT_ID('TEMPDB.dbo.#Local_nums') IS NOT NULL DROP TABLE #Local_nums
IF OBJECT_ID('TEMPDB.dbo.#TestInvestments_LatestAvailable_Local') IS NOT NULL DROP TABLE #TestInvestments_LatestAvailable_Local

/*create temp table for USD Holdings only - Local Currency is unioned later*/
Select * into #TestInvestments_Local_Only from TestInvestments where [Currency Type] = 'Local' 


--create temp table to hold the local missing holdings
CREATE TABLE #TestInvestments_LatestAvailable_Local(
  [Reference Date] date  NOT NULL
  ,Investment nvarchar (200) NOT NULL
  ,[Holding Key] int NOT NULL
  ,Holding nvarchar (200) NOT NULL
  ,[Currency Type] [varchar](100) NULL
  ,[Data Availability] varchar(100) NULL
  ,[Current Value] [float] NULL
  ,[Current Cost] [float] NULL
  ,[Realized] [float] NULL
  ,primary key(Investment, [Holding Key], [Reference Date])
);

--cartesian join to create a table of row numbers (65,536 rows)
create table #Local_nums(n integer not null primary key);
with
  p1 as (select 0 as n union all select 0), -- 2
  p2 as (select 0 as n from p1 cross join p1 as b), -- 4
  p3 as (select 0 as n from p2 cross join p2 as b), -- 16
  p4 as (select 0 as n from p3 cross join p3 as b), -- 256
  p5 as (select 0 as n from p4 cross join p4 as b) -- 65536
insert into #Local_nums(n) select row_number() over(order by (select 0)) from p5;

--Create Variables for all of the USD columns to store the missing rows
declare

@local_prev_Investment nvarchar(200) ,@local_Investment nvarchar(200),
@local_prev_HoldingKey int, @local_HoldingKey int,
@local_prev_ReferenceDate date ,@local_ReferenceDate date,
@local_prev_Holding nvarchar(200) ,@local_Holding nvarchar(200),
@local_prev_CurrencyType varchar(100) ,@local_CurrencyType varchar(100),
@local_prev_DataAvailability varchar(100),@local_DataAvailability varchar(100),
@local_prev_CurrentValue float ,@local_CurrentValue float,
@local_prev_CurrentCost float ,@local_CurrentCost float,
@local_prev_Realized float ,@local_Realized float,
@local_qdiff integer;
    
--Begin loop for USD to look for missing rows and add them as variables
declare
  d cursor forward_only static read_only for
    select Investment, [Holding Key], [Reference Date],Holding,[Currency Type],[Data Availability],[Current Value],[Current Cost],[Realized]
     from #TestInvestments_Local_Only
    union all
    select Investment, [Holding Key], ref_date, NULL,   NULL,   NULL,   NULL,  NULL,  NULL
    from (select dateadd(q,1,max([reference date])) as ref_date from #TestInvestments_Local_Only) as a
    cross join (select distinct Investment from #TestInvestments_Local_Only) as b
    cross join (select distinct [Holding Key] from #TestInvestments_Local_Only) as d
    --cross join (select distinct [Currency Type] from #TestInvestments_Local_Only) as d
    order by Investment, [Holding Key], [Reference Date], [Current Value] desc;
open d;

--Set the most recent data = to the reference date we are currently looking at (if missing data, then set the latest data = current quarter in the loop)
fetch next from d into @local_Investment,@local_HoldingKey,@local_ReferenceDate,@local_Holding,@local_CurrencyType,@local_DataAvailability,@local_CurrentValue,@local_CurrentCost,@local_Realized;  
select
    @local_prev_Investment=@local_Investment,
    @local_prev_HoldingKey=@local_HoldingKey,
    @local_prev_ReferenceDate=@local_ReferenceDate,
    @local_prev_Holding=@local_Holding,
    @local_prev_CurrencyType=@local_CurrencyType,
    @local_prev_DataAvailability=@local_DataAvailability,
    @local_prev_CurrentValue=@local_CurrentValue,
    @local_prev_CurrentCost=@local_CurrentCost,
    @local_prev_Realized=@local_Realized;

--Insert the missing data into the temp table
while @@fetch_status = 0 begin
  fetch next from d into @local_Investment,@local_HoldingKey,@local_ReferenceDate,@local_Holding,@local_CurrencyType,@local_DataAvailability,@local_CurrentValue,@local_CurrentCost,@local_Realized;
  set @local_qdiff = datediff(q, @local_prev_ReferenceDate, @local_ReferenceDate);
  if @local_prev_Investment = @local_Investment and
     @local_prev_HoldingKey = @local_HoldingKey and
     @local_qdiff > 1
  begin
    insert into #TestInvestments_LatestAvailable_Local([Investment],[Holding Key],[Reference Date],[Holding],[Currency Type],[Data Availability],[Current Value],[Current Cost],Realized)
  select
        @local_prev_Investment,@local_prev_HoldingKey,eomonth(dateadd(q, #Local_nums.n, @local_prev_ReferenceDate)),@local_prev_Holding,@local_CurrencyType,@local_DataAvailability,@local_CurrentValue,@local_CurrentCost,@local_Realized  
      from #Local_nums
      where #Local_nums.n < @local_qdiff + iif(@local_CurrentValue = -1, 1, 0);
  end;
  select
    @local_prev_Investment=@local_Investment,
    @local_prev_HoldingKey=@local_HoldingKey,   
    @local_prev_ReferenceDate=@local_ReferenceDate,
    @local_prev_Holding=@local_Holding,
    @local_prev_CurrencyType=@local_CurrencyType,
    @local_prev_DataAvailability=@local_DataAvailability,
    @local_prev_CurrentValue=@local_CurrentValue,
    @local_prev_CurrentCost=@local_CurrentCost,
    @local_prev_Realized=@local_Realized;
end
close d
deallocate d

/*Had to add an extra quarter to get the maximum reference date for the current quarter, otherwise the max date was the max date that the fund reported
Below, I delete anything that went past the current MAX reference date for ALL FUNDS (not just a specific fund)*/

DELETE FROM #TestInvestments_LatestAvailable_Local WHERE [Reference Date] >= (select DATEADD(q,1,MAX([Reference Date])) from TestInvestments)
DELETE FROM #TestInvestments_LatestAvailable_USD WHERE [Reference Date] >= (select DATEADD(q,1,MAX([Reference Date])) from TestInvestments)

/*statement to insert missing holdings into current TestInvestments table*/

Insert into TestInvestments ([Data Availability],[Investment],[Holding Key],[Holding],[Reference Date],[Currency Type],[Current Value],[Current Cost],Realized)
        select  [Data Availability] = 'Include Latest from Previous Submissions',[Investment],[Holding Key],[Holding],[Reference Date],[Currency Type],[Current Value],[Current Cost],Realized
        from #TestInvestments_LatestAvailable_Local
        union
        select [Data Availability] = 'Include Latest from Previous Submissions',[Investment],[Holding Key],[Holding],[Reference Date],[Currency Type],[Current Value],[Current Cost],Realized
        from #TestInvestments_LatestAvailable_USD

非常感谢您提供的任何帮助!

很难确定你的场景是不是有点模棱两可。从你的评论来看,你似乎只是想做两件事:

  1. 每个季度的每个组都有一行,即使不存在数据也是如此
  2. 对于没有数据的地方,拉入最近的历史值(交叉应用是完美的)

解决方案

;WITH cte_GenerateQuarters AS (
    SELECT [Reference Date] = CAST('2019-12-31' AS DATE) /*Start date*/
    UNION ALL
    SELECT EOMONTH(DATEADD(qq,1,[Reference Date]))
    FROM cte_GenerateQuarters
    WHERE [Reference Date] < '2022-06-30' /*End date*/
),
cte_DistinctGroups AS (
    /*1 row for each "group"*/
    SELECT Investment,Holding,[Currency Type]
    FROM TestInvestments
    GROUP BY Investment,Holding,[Currency Type]
),
cte_DistinctRows AS (
    /*Generates 1 row per each date,investment,holding,currency type*/
    SELECT A.*,b.[Reference Date]
    FROM cte_DistinctGroups AS A
    CROSS JOIN cte_GenerateQuarters AS B
    )
SELECT
A.Investment
,A.Holding
,A.[Currency Type]
,A.[Reference Date]
,Realized = ISNULL(B.Realized,C.Realized)
,CASE WHEN B.Realized IS NOT NULL THEN 'Realized = Reported Value' ELSE 'Realized = Pulled from history' END
FROM cte_DistinctRows AS A
LEFT JOIN TestInvestments AS B /*Reported data for specific quarter*/
    on A.Investment = B.Investment
    AND A.Holding = B.Holding
    AND A.[Currency Type] = B.[Currency Type]
    AND A.[Reference Date] = B.[Reference Date]
OUTER APPLY (
    /*Pulls in historical quarter data*/
    SELECT TOP(1) *
    FROM TestInvestments AS DTA
    WHERE DTA.Investment = A.Investment
    AND DTA.Holding = A.Holding
    AND DTA.[Currency Type] = A.[Currency Type]
    AND A.[Reference Date] >= DTA.[Reference Date]
    ORDER BY DTA.[Reference Date] DESC
) AS C
OPTION (MAXRECURSION 0) /*Used to in case you need to generate more than 25 quarters via recursion*/

我最终能够使用基于集合的解决方案来填补数据中的空白并将数据带到今天,以取代我以前使用的游标,这太细化了(基于缺失的持有,而不是缺失的投资并根据该投资填充最新持有量):

/*                    -----------First part of code only fills the gaps in the data - does not bring forward data to current date---------                  */


/*Get max date to bring holdings forward*/
Drop table if exists #MaxDatefromUholdings
Select Max([Reference Date]) as MaxD 
into #MaxDatefromUholdings from TestInvestmentsFixed

Drop table if exists #Quarter
select distinct LastDayOfQuarter into #Quarter
from trsinvsqlp.ctrl.dbo.datedim
where LastDayOfQuarter between '3/1/2018' and (Select MaxD from #MaxDatefromUholdings) --First date we received holdings to max date of the portfolio

Drop table if exists #MissingHolding
/*Get first date an investment was submitted so cross join doesn't populate dates from the datedim table before this*/
;with tbl_Holding as (
    select  holding,
            investment,
            [Currency Type],
            min([Reference Date]) ReferenceDateMin,
            max([Reference Date]) ReferenceDateMax
    from TestInvestmentsFixed
    group by holding, investment, [Currency Type]
)
/*Get all combinations of dates and holdings*/
select  tbl_Holding.holding,
        tbl_Holding.investment,
        tbl_Holding.[Currency Type],
        lastdayofquarter,
        tbl_Holding.ReferenceDateMax,
        ReferenceDateMin 
into #MissingHolding
from tbl_Holding
    cross join #Quarter
where 1 = 1
    and #Quarter.LastDayOfQuarter >= tbl_Holding.ReferenceDateMin /*Filter cross join - Grab latest available greater than 1st time an investment submitted*/
    and #Quarter.LastDayOfQuarter <= ReferenceDateMax /*LastDayofQuart less than last submission date by investment - omit data where a manager hasn't submitted yet - this is taken care of in second part of code*/
    and not exists (
        select 1
        from TestInvestmentsFixed tbl_ActualHolding
        where tbl_Holding.[Currency Type] = tbl_ActualHolding.[Currency Type]
         and tbl_Holding.investment = tbl_ActualHolding.investment
         --and tbl_Holding.Holdingskey = tbl_ActualHolding.Holdingskey
         and #Quarter.LastDayOfQuarter = tbl_ActualHolding.[Reference Date]
    ) --Finds data in the cross joined table, not currently in TestInvestmentsFixed

Drop table if exists #HoldingsGapsFilled
;with tbl_MissingHolding as (
    select  #MissingHolding.holding as L_holding,
            [L_Data Availability Missing] = 'Include Latest from Previous Submissions',
            #MissingHolding.investment as L_investment,
            #MissingHolding.[Currency Type] as [L_Currency Type],
            #MissingHolding.LastDayOfQuarter,
            #MissingHolding.ReferenceDateMin,
            max(TestInvestmentsFixed.[Reference Date]) ReferenceDateLast
    from #MissingHolding
        join TestInvestmentsFixed
         on #MissingHolding.holding=TestInvestmentsFixed.Holding
          and #MissingHolding.investment = TestInvestmentsFixed.Investment
          and #MissingHolding.[Currency Type] = TestInvestmentsFixed.[currency type]
          and #MissingHolding.LastDayOfQuarter > TestInvestmentsFixed.[Reference Date] /*Pulls in latest within data gaps available only*/
    group by #MissingHolding.holding,
            #MissingHolding.investment,
            #MissingHolding.[Currency Type],
            #MissingHolding.LastDayOfQuarter,
            #MissingHolding.ReferenceDateMin
)
/*Add rest of columns from TestInvestmentsFixed to latest available data*/
select tbl_MissingHolding.*,TestInvestmentsFixed.*
into #HoldingsGapsFilled
from tbl_MissingHolding
    join TestInvestmentsFixed
     on tbl_MissingHolding.L_holding=TestInvestmentsFixed.Holding
          and tbl_MissingHolding.L_investment = TestInvestmentsFixed.Investment
          and tbl_MissingHolding.[L_Currency Type] = TestInvestmentsFixed.[currency type]
          and tbl_MissingHolding.ReferenceDateLast = TestInvestmentsFixed.[Reference Date]

/*         ----------Second part of code brings forward investments from their max submission date from the last date that an investment submitted---------                  */

Drop table if exists #HoldingsSinceLatestSubmission

--Get last submission date for each investment
;With AddFutureStuff as (
    select distinct 
            investment as Distinct_Investment,
            max([Reference Date]) LastSubmissionDate
    from TestInvestmentsFixed
    group by investment
    ),
    --Get max date for entire portfolio
    MaxDateofPortfolio as (
    Select max([Reference Date]) as MaxPortfolioDate
    from TestInvestmentsFixed
    ),
    --Combine last date an investment submitted with the max date for the entire portfolio
    DatesBetween as ( 
    select Distinct_Investment, LastSubmissionDate, (select MaxPortfolioDate from MaxDateofPortfolio) as MaxPortfolioDate
    from AddFutureStuff
    )
    select #Quarter.LastDayOfQuarter, 
            DatesBetween.*,
            TestInvestmentsFixed.* 
    into #HoldingsSinceLatestSubmission
    from DatesBetween
    --Join TestInvestmentsFixed to pull in all other columns from TestInvestmentsFixed with last submission dates and max portfolio date
    join TestInvestmentsFixed 
    on TestInvestmentsFixed.[Reference Date] = DatesBetween.LastSubmissionDate
    and TestInvestmentsFixed.investment = DatesBetween.Distinct_Investment
    --Cross join to populate table for all dates between last submission date (by investment) and max portfolio date
    cross join #Quarter
    where #quarter.LastDayOfQuarter > LastSubmissionDate and #quarter.LastDayOfQuarter <= MaxPortfolioDate --Ensures cross join is only between latest submission and max Portfolio date
    and LastSubmissionDate <> MaxPortfolioDate --Remove any holdings submitted in the current quarter
    order by TestInvestmentsFixed.Investment, #Quarter.LastDayOfQuarter