I am trying to rewrite the "Query" part by removing the function.
Since the table has more than 800k records, the function is called thrice for all 800k records.
I want to eliminate function and replace with JOIN?
--Function
CREATE FUNCTION [dbo].[MapValue]
(
#ObjCode AS INT,
#ObjName AS VARCHAR(50),
#ObjValue AS INT
)
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE #returnValue AS VARCHAR(100)
SELECT #returnValue = Value FROM dbo.tblMap
WHERE ObjCode = #ObjCode AND ObjName = #ObjName AND ObjValue = #ObjValue
-- Return the result of the function
RETURN #returnValue
END
--Query
SELECT ObjectId, ObjectTypeCode,
dbo.MapValue(4, 'ACode', ACode) AS AType,
dbo.MapValue(4, 'SCode', SCode) AS SCode,
dbo.MapValue(4, 'PCode', PCode) AS PCode
FROM dbo.APoint
WHERE ObjectTypeCode = 1
Here is the query that I came up using JOIN.
Is there any better way to do this? Can we use just ONE join instead of three?
--Modified query
SELECT ObjectId, ObjectTypeCode,
A.Value,
s.Value,
p.Value
FROM dbo.APoint ap
LEFT JOIN tblMap A ON A.ObjCode = 4 AND A.ObjName = 'ACode' AND A.ObjValue = ap.TypeCode
LEFT JOIN tblMap s ON s.ObjCode = 4 AND s.ObjName = 'SCode' AND s.ObjValue = ap.TypeCode
LEFT JOIN tblMap p ON p.ObjCode = 4 AND p.ObjName = 'PCode' AND p.ObjValue = ap.TypeCode
WHERE ObjectTypeCode = 1
You could use case and a single left join:
SELECT ObjectId, ObjectTypeCode,
CASE WHEN map.ObjName = 'ActivityTypeCode' THEN map.Value END AS AType,
CASE WHEN map.ObjName = 'statecode' THEN map.Value END As SCode,
CASE WHEN map.ObjName = 'PriorityCode' THEN map.Value END As PCode
FROM dbo.APoint ap
LEFT JOIN tblMap map ON map.ObjCode = 4
AND map.ObjName IN('ActivityTypeCode', 'statecode', 'PriorityCode')
AND map.ObjValue = ap.ActivityTypeCode
WHERE ObjectTypeCode = 1
GROUP BY ObjectId, ObjectTypeCode
I have a VERY long running, heavy stored procedure, so I would like to change it over to a job that runs every night and updates a table so the user can run it quickly during the day. My problem is that it takes parameters IN THE LEFT JOIN, so I don't see how I could do it. I tried to remove the parameters from the joins and just take all the records, but first problem with that is that these left joins are joins to table valued functions with group by clauses (so it's tons of records now) and also when I did it, it crashed my whole server. I will post part of my stored proc to give you an idea of what i'm doing for now.
--alternatively, i would take an answer that could change my query perfomance from 2 minutes to 10 seconds. :)
--btw, another thing that is slowing down this query is that the functions are querying a linked server...
create PROC [dbo].[mySP]
#FromDate DateTime,
#ToDate DateTime = NULL,
#Style NVARCHAR(max),
#ItemType NVARCHAR(300),
#ItemCode NVARCHAR(150)= null,
#ItemsNotSold INT,
#MultiplyUsage DECIMAL(18,4),
#SParts BIT,
#vendorId nvarchar(50),
#currentPage int,
#largeOrderSoNumbers nvarchar(300) = null,
#excludeprojTag BIT,
#hasOrderQty BIT = null,
#createPO BIT = null,
#excludeTestOrders BIT = null,
#refresh BIT = null,
#userid int = null,
#positiveOverUse BIT = null
AS
SELECT #FROMDATE = #FROMDATE
,#ToDate=#ToDate
,#Style=#Style
,#ItemType=#ItemType
,#ItemCode=#ItemCode
,#ItemsNotSold=#ItemsNotSold
,#MultiplyUsage=#MultiplyUsage
,#SParts=#SParts
,#vendorId=#vendorId
,#currentPage=#currentPage
,#largeOrderSoNumbers=#largeOrderSoNumbers
,#excludeprojTag=#excludeprojTag
,#hasOrderQty =#hasOrderQty
,#createPO =#createPO
,#excludeTestOrders =#excludeTestOrders
,#refresh =#refresh
,#userid =#userid
,#positiveOverUse=#positiveOverUse
DECLARE #amountPerPage int = 400;
DECLARE #startingPoint int = (#currentpage - 1) * #amountPerPage
SET #TODATE = NULLIF(#TODATE,'1/1/4000');
SET #ITEMCODE = NULLIF(#ITEMCODE,'');
SET #largeOrderSoNumbers = NULLIF(#LARGEORDERSONUMBERS,'');
BEGIN
IF #Style = '-1'
SET #Style = NULL
IF #ItemType = '-1'
SET #ItemType = NULL
END
SELECT
ISNULL(c.NewQB_ListID,iit.ListID) ListID,
(select salesdesc from iteminventory i where i.listid = ISNULL(c.NewQB_ListID,iit.ListID)) as SalesDesc,
isnull(inl.linename,'') + isnull(c.NewName,Name) as ItemCode ,
max(il.CubicMeterKD) as CubicMeterKD,
sum(CONVERT(decimal(18,4),inl.Quantity) )
- sum(CONVERT(decimal(18,4),inl.qtyRuleTag))
+ CONVERT(decimal(18,4),isnull(ai.qty,0))
-CONVERT(decimal(18,4),isnull(ai.QtyRuleTag,0))
AS TotalQtyUesd,
sum(convert(decimal(18,4),isnull(inl.QtyRuleTag,0)))
as QtyUsedWithProjectTag,
Convert(decimal(18,4),QuantityOnHand) as OnHand,
CONVERT(int,ISNULL(EzQtyOnSO.Qty,0)) as OnSalesOrder,
CONVERT(int,ISNULL(EzQtyAnyStatus.QtyLgO,0))as OnLgOrder,
Convert(decimal(18,4),QuantityOnOrder) as OnPO,
Convert(decimal(18,4),isnull(LargePO.lgOrderQtyOnPO,0)) as lgOrderQtyOnPO ,
fl.LineName
INTO #Q
FROM
iteminventory iit
LEFT JOIN Lines fl
ON iit.ParentRef_ListID = QB_LisiID
LEFT JOIN tb_ItemList il
ON iit.ListID = il.QBListID
LEFT JOIN InlSales(#FromDate,#ToDate, #excludeprojtag, #excludeTestOrders) INL
ON IIT.LISTID = INL.ITEMREF_LISTID
LEFT JOIN tb_CombinedItems c
ON iit.ListID = c.ListID
LEFT JOIN [QuantityInvoice](#FromDate, #ToDate,#excludeprojTag,#excludeTestOrders) as ai
ON ai.QBID = iit.ListID
LEFT JOIN fn_QuantityOnSalesOrder(#excludeTestOrders) AS EzQtyOnSO
ON iit.ListID = EzQtyOnSO.QBID
LEFT JOIN QuantityAnyStatus(#largeOrderSoNumbers, #excludeprojTag) AS EzQtyAnyStatus
ON iit.ListID = EzQtyAnyStatus.QBID
LEFT JOIN dbo.[FN_LargePO](#excludeTestOrders) LargePO --WITH (nolock)
ON LargePO.QBID = iit.ListID
WHERE
(#Style is null or ISNULL(c.ParentListID,ParentRef_ListID) in (SELECT Value From fn_MultiValueParameter(#Style))) AND
(#ItemType is null or ItemTypeCode in (SELECT Value From fn_MultiValueParameter(#ItemType))) AND
(#ItemCode is null or il.ItemCode like '%' + #ItemCode + '%') AND
(IsActive = 1 OR c.ListID IS NOT NULL)
GROUP BY
ISNULL(c.NewQB_ListID,iit.ListID),
ISNULL(c.NewName,Name),
inl.LineName,
QuantityOnHand,
iit.QuantityOnOrder,
CONVERT(int,ISNULL(EzQtyOnSO.Qty,0)),
fl.LineName,
CONVERT(decimal(18,4),isnull(ai.qty,0)),
Convert(decimal(18,4),isnull(LargePO.lgOrderQtyOnPO,0)),
CONVERT(int,ISNULL(EzQtyAnyStatus.QtyLgO,0)),
CONVERT(decimal(18,4),isnull(ai.QtyRuleTag,0))
IF #ItemsNotSold = 1
BEGIN
DELETE FROM #Q
WHERE isnull(TotalQtyUesd,0) <> 0
END
IF #ItemsNotSold = 2
BEGIN
DELETE FROM #Q
WHERE isnull(TotalQtyUesd,0) = 0
END
SELECT
q.ListID,
q.SalesDesc,
q.ItemCode,
MAX(isnull(q.CubicMeterKD,0)) as CubicMeterKD,
SUM(q.TotalQtyUesd)
TotalQtyUesd,
SUM(q.QtyUsedWithProjectTag) as QtyUsedWithProjectTag,
SUM(q.OnHand) OnHand,
SUM(q.OnSalesOrder) OnSalesOrder,
SUM(q.OnLgOrder) OnLgOrder,
SUM(q.OnPO)
- isnull(SUM(q.lgOrderQtyOnPO),0)
OnPO,
SUM(q.lgOrderQtyOnPO) as lgOrderQtyOnPO,
LineName
INTO #QTY
FROM
#Q q
GROUP BY
q.ListID,
q.salesdesc,
q.ItemCode,
q.LineName
;WITH Results AS
(
SELECT
isnull(P.listid,'') as ListID,
isnull(P.SalesDesc,'') as SalesDesc,
isnull(P.itemcode,'') as ItemCode,
ISNULL(CubicMeterKD,0) AS CubicMeterKD,
isnull(TotalQtyUesd,0) as TotalQtyUsed,
isnull(QtyUsedWithProjectTag,0) as QtyUsedWithProjectTag,
isnull(onhand,0) as OnHand,
isnull((OnHand - OnSalesOrder - OnLgOrder),0) as available,
isnull(OnLgOrder,0) as OnLgOrder,
isnull(OnPO,0) as OnPo,
isnull(lgOrderQtyOnPO,0) as lgOrderQtyOnPO,
isnull(((OnHand - OnSalesOrder) + OnPO) ,0) AS [AvailableAndOnPo],
isnull((TotalQtyUesd * #MultiplyUsage) - ((OnHand - OnSalesOrder) + OnPO),0) AS [AvlOnPOminusUsed],
isnull((((TotalQtyUesd * #MultiplyUsage) - ((OnHand - OnSalesOrder) + OnPO)) / CASE WHEN TotalQtyUesd > 0 THEN TotalQtyUesd END)*100,0) AS PctOver
,isnull(linename,0) as LineName,
isnull((select price from qbdb.dbo.tb_iteminfodetail where vendorlistid = #vendorid and itemcode =isnull(P.itemcode,'')),0.0) as price
,isnull(pod.qtytoReOrder,0) as qtyToReOrder
,isnull(pod.qtytoOrder,0) as qtyToOrder
,isnull(pod.includePO,0) as includePO
,ROW_NUMBER() OVER (ORDER BY ITEMCODE) AS RowNum
FROM #qty AS p
left join dbo.purchaseorderpreliminarydetails pod on pod.listid = P.listid and pod.deleteFlag = 0
and headerid = (select top 1 headerid from purchaseorderpreliminary where deleteFlag = 0)
WHERE
CASE WHEN #positiveOverUse = 1 THEN
isnull((((TotalQtyUesd * #MultiplyUsage) - ((OnHand - OnSalesOrder) + OnPO)) / CASE WHEN TotalQtyUesd > 0 THEN TotalQtyUesd END)*100,0)
ELSE 1 END >0
AND
case when #hasOrderQty = 1 then isnull(pod.QtyToReOrder,0) else 1 end > 0
and
CASE WHEN #SPARTS = 1 THEN CASE WHEN
ItemCode IN (
'APP',
'CD',
'-S',
'L0',
'L/42',
'L01',
'Lfrgs2',
'Lfad2',
'Sfasdf9',
'SdafdsA',
'Sfasdf3',
'Sasdf6',
'asdf0',
'Sf6',
'fasdfadf2',
'fasdfasdf',
'S2236',
'S12342',
'Sdf 30',
'SdfE 36',
'fgsfgs',
'fasdf-fdasf',
'fadf',
'fasdf-fasdf',
'sdaf',
'adf 11"',
'fda 14"',
'fdas 24"') THEN 1 ELSE 0 END ELSE 1 END = 1
)
SELECT (select count(rownum) from results) as totalItems,* FROM Results WHERE
rownum between #startingPoint + 1 and #startingPoint + #amountPerPage
Have a form the users can put their parameters into before they go home, and save them into a new table in the database. Change the stored procedure to either get the parameters from this new table or to use the new table in the joins &c. The SP now doesn't need the parameters to sent to it. Finally call the SP via a Job.
The following query works. However, when I uncomment the commented lines in order to assign query variables to my locally-defined variables, I get a syntax error at the final ";".
DECLARE #tmp_total_ct int = 0;
DECLARE #tmp_closed_ct int = 0;
--SELECT #tmp_total_ct = tot_ct, #tmp_closed_ct = cls_ct
-- FROM (
SELECT COUNT(*) AS tot_ct,
SUM(IIF(is_closed = 1, 1, 0)) AS cls_ct
FROM corp.CashDealDetails
INNER JOIN WidgeStores wstrs
ON corp.CashDealDetails.abbrev = wstrs.abbrev
WHERE mall_id IN
(SELECT mall_ndx FROM WidgeStores wstrs
WHERE wstrs.abbrev IN
(SELECT abbrev FROM #tmpMkts)
AND wstrs.[is_opn] = 1
)
AND YEAR(closing_dt) = 2012
--);
I believe that I'm using the correct syntax for assigning query values to user-defined variables, as explained in other posts on these threads. Where exactly is the syntax error?
declare #tmp_total_ct int
declare #tmp_closed_ct int
SELECT #tmp_total_ct = tot_ct, #tmp_closed_ct = cls_ct
FROM (
SELECT COUNT(*) AS tot_ct,
SUM(IIF(is_closed = 1, 1, 0)) AS cls_ct
FROM corp.CashDealDetails
INNER JOIN WidgeStores wstrs
ON corp.CashDealDetails.abbrev = wstrs.abbrev
WHERE mall_id IN
(SELECT mall_ndx FROM WidgeStores wstrs
WHERE wstrs.abbrev IN
(SELECT abbrev FROM #tmpMkts)
AND wstrs.[is_opn] = 1
)
AND YEAR(closing_dt) = 2012
) a
print #tmp_total_ct
print #tmp_closed_ct
Can you give me some pointers (or point in the right direction on what search terms for google)? In a stored procedure I have a parameter #TAG (string). I receive '(14038314,14040071)' (for example) from another application that cannot be altered. In the stored procedure, I need to split apart '(14038314,14040071)' to put quotes around each string value, rebuild it, strip out the outer quotes,strip out the parens and pass it to #TAG in the query below so that it looks like the line commented out below?
SELECT
V.NAME AS VARIETY, TAGID
FROM
mfinv.dbo.onhand h
INNER JOIN
mfinv.dbo.onhand_tags t on h.onhand_id = t.onhand_id
INNER JOIN
mfinv.dbo.onhand_tag_details d on t.onhand_tag_id = d.onhand_tag_id
INNER JOIN
mfinv.dbo.FM_IC_PS_VARIETY V ON V.VARIETYIDX = d.VARIETYIDX
LEFT JOIN
mfinv.dbo.FM_IC_TAG TG ON TG.TAGIDX = t.TAGIDX
WHERE
h.onhand_id = (SELECT onhand_id FROM mfinv.dbo.onhand
WHERE onhand_id = IDENT_CURRENT('mfinv.dbo.onhand'))
AND TG.ID IN (#TAG)
--AND TG.ID IN ('14038314','14040071')
You can Use Dynamic SQL Like This
DECLARE #TAG Nvarchar(MAX)='14038314,14040071'
set #TAG=''''+REPLACE(#TAG,',',''',''')+''''
--select #TAG
DECLARE #SQL NVARCHAR(MAX)=N'
Select V.NAME AS VARIETY, TAGID
FROM mfinv.dbo.onhand h
INNER JOIN mfinv.dbo.onhand_tags t on h.onhand_id = t.onhand_id
INNER JOIN mfinv.dbo.onhand_tag_details d on t.onhand_tag_id = d.onhand_tag_id
INNER JOIN mfinv.dbo.FM_IC_PS_VARIETY V ON V.VARIETYIDX = d.VARIETYIDX
LEFT JOIN mfinv.dbo.FM_IC_TAG TG ON TG.TAGIDX = t.TAGIDX
WHERE h.onhand_id = (SELECT onhand_id FROM mfinv.dbo.onhand
WHERE onhand_id = IDENT_CURRENT (''mfinv.dbo.onhand''))
AND TG.ID IN ('+#TAG+')'
PRINT #SQL
EXEC (#SQL)
Here's what I did. Thank you all for responding. Thanks to dasblinkenlight for answering "How to replace first and last character of column in sql server?". Thanks to SQLMenace for answering "How Do I Split a Delimited String in SQL Server Without Creating a Function?".
Here's how I removed parenthesis:
#Tag nvarchar(256)
SET #Tag = SUBSTRING(#Tag, 2, LEN(#Tag)-2)
Here's how I split and rebuilt #Tag:
AND TG.ID in
(
SELECT SUBSTRING(',' + #Tag + ',', Number + 1,
CHARINDEX(',', ',' + #Tag + ',', Number + 1) - Number -1)AS VALUE
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #Tag + ',') - 1
AND SUBSTRING(',' + #Tag + ',', Number, 1) = ','
)
Our DBA has changed a function to be a procedure, so I am amending some of my procedures to cater for this. I've come across a problem with this in one of my procedures where I have a while loop.
I populate my temp table from the new procedure (#DGContol), and then have the following while loop:
SELECT #MinRcd = MIN(RcdNum) FROM #PortfolioDisclosure
SELECT #MaxRcd = MAX(RcdNum) FROM #PortfolioDisclosure
SET #RcdNum = #MinRcd
WHILE #RcdNum <= #MaxRcd
BEGIN
-- Temporarily assign values to variables
SELECT
#PortfolioGroup = PortfolioCode
, #EndDateTime = MaxPositionDate_DetailedDisclosure
FROM #PortfolioDisclosure
WHERE RcdNum = #RcdNum
INSERT INTO #PositionsTable
SELECT
fi.ID_ISIN AS [Fund_ISIN]
, RTRIM(a.acct_id) AS [Internal_Portfolio_Code]
, a.acct_desc AS [Portfolio/Fund_Name]
, CONVERT(CHAR(11),p.as_of_tms,103) AS [Portfolio_Date]
, a.alt_curr_cde AS [Portfolio_Base_Ccy]
, i.iss_desc AS [Security_Description]
, RTRIM(i.pref_iss_id) AS [Security_ID SEDOL/Internal]
, RTRIM(ia.iss_id) AS [Security_ID ISIN]
, i.denom_curr_cde AS [Denomination_Ccy]
, SUM(p.valval_alt_cmb_amt) OVER (PARTITION BY RTRIM(a.acct_id))
AS [Total_Fund_Value]
, p.orig_quantity AS [Shares/Par_Value]
, p.valval_alt_cmb_amt AS [Market_Value]
, p.fld5_rte AS [Pct_of_NAV]
, SUM(CASE WHEN i.issue_cls1_cde = '010' THEN p.valval_alt_cmb_amt ELSE 0 END) OVER (PARTITION BY a.acct_id)
AS [Cash/Cash_Equivalents]
, i.inc_proj_cmb_rte AS [Coupon_Rate]
, CONVERT(CHAR(11),i.mat_exp_dte,103) AS [Maturity_Date]
FROM dw_position_dg AS p
INNER JOIN #DGControl AS dgc -- [M]onthly, [M]ost recent position
ON dgc.DataGrpCtlNum = p.data_grp_ctl_num
INNER JOIN dw_ivw_acct AS a WITH (NOLOCK)
ON a.acct_id = p.acct_id
INNER JOIN dw_issue_dg AS i WITH (NOLOCK)
ON i.instr_id = p.instr_id
LEFT OUTER JOIN dw_issue_alt_id AS ia WITH (NOLOCK)
ON ia.instr_id = i.instr_id
AND ia.id_ctxt_typ = 'ISIN'
INNER JOIN #PortfolioDisclosure AS fi
ON fi.PortfolioCode = p.acct_id
and fi.MaxPositionDate_DetailedDisclosure >= p.as_of_tms
WHERE RTRIM(a.acct_id) NOT IN ( SELECT xref.internal_value FROM dbo.DP_CrossReference as xref
WHERE xref.Codeset_type_id = 10401
AND xref.Originator_id = 'DataVendorPortfolioExclusion')
-- Clear down variable values
SET #PortfolioGroup = NULL
--SET #StartDateTime = NULL
SET #EndDateTime = NULL
-- Move to next record
SET #RcdNum = #RcdNum + 1
END -- END WHILE LOOP
However this returns lots of duplicate records. If I replace the temp table #DGControl with the original function then I get the correct number of records.
I don't really know what the issue would be or how I could re code this while loop so that using the table #DGControl I get the correct number of records. Can anyone help?
Have you compared the output of SELECT * FROM OldFunction(args..) with EXEC NewStoredProcedure args...? If so, does the data returned look the same or has the duplication crept in when the DBA refactored the function to a proc.
If so you may need to de dupe the output from the sp first then run it through your remaining code. In short, if you are using the same arguments for both but they give you different results then you'll need to go back to the DBA.
You aren't using either of your local variables (#PortfolioGroup, #EndDateTime) in this code. It is just inserting the same record set N number of times. Also, I think you can write this as a single select query without using temp tables or while loops and it will make it less confusing.
Thank you for your feedback. i worked out the issue. My code looks like the following now:
BEGIN
SELECT #MinRcd = MIN(RcdNum) FROM #PortfolioDisclosure
SELECT #MaxRcd = MAX(RcdNum) FROM #PortfolioDisclosure
SET #RcdNum = #MinRcd
WHILE #RcdNum <= #MaxRcd
BEGIN
-- Temporarily assign values to variables
SELECT
#PortfolioGroup = PortfolioCode
, #EndDateTime = MaxPositionDate_DetailedDisclosure
FROM #PortfolioDisclosure
WHERE RcdNum = #RcdNum
-- Insert DGControl record into table based on the MaxPositionDate_DetailedDisclosure from Portfolio Disclosure function
INSERT INTO #DGControl
EXEC InfoPortal..usp_Generic_DGControl '', '', #PortfolioGroup, '1', #EndDateTime, #EndDateTime, #PeriodType, 'M', 'POSITION' -- [M]onthly, [M]ost recent position
-- Insert into #PositionsTable
INSERT INTO #PositionsTable
SELECT
fi.ID_ISIN AS [Fund_ISIN]
, RTRIM(a.acct_id) AS [Internal_Portfolio_Code]
, a.acct_desc AS [Portfolio/Fund_Name]
, CONVERT(CHAR(11),p.as_of_tms,103) AS [Portfolio_Date]
, a.alt_curr_cde AS [Portfolio_Base_Ccy]
, i.iss_desc AS [Security_Description]
, RTRIM(i.pref_iss_id) AS [Security_ID SEDOL/Internal]
, RTRIM(ia.iss_id) AS [Security_ID ISIN]
, i.denom_curr_cde AS [Denomination_Ccy]
, SUM(p.valval_alt_cmb_amt) OVER (PARTITION BY RTRIM(a.acct_id))
AS [Total_Fund_Value]
, p.orig_quantity AS [Shares/Par_Value]
, p.valval_alt_cmb_amt AS [Market_Value]
, p.fld5_rte AS [Pct_of_NAV]
, SUM(CASE WHEN i.issue_cls1_cde = '010' THEN p.valval_alt_cmb_amt ELSE 0 END) OVER (PARTITION BY a.acct_id)
AS [Cash/Cash_Equivalents]
, i.inc_proj_cmb_rte AS [Coupon_Rate]
, CONVERT(CHAR(11),i.mat_exp_dte,103) AS [Maturity_Date]
FROM dw_position_dg AS p
INNER JOIN #DGControl AS dgc
ON dgc.DataGrpCtlNum = p.data_grp_ctl_num
INNER JOIN dw_ivw_acct AS a WITH (NOLOCK)
ON a.acct_id = p.acct_id
INNER JOIN dw_issue_dg AS i WITH (NOLOCK)
ON i.instr_id = p.instr_id
LEFT OUTER JOIN dw_issue_alt_id AS ia WITH (NOLOCK)
ON ia.instr_id = i.instr_id
AND ia.id_ctxt_typ = 'ISIN'
INNER JOIN #PortfolioDisclosure AS fi
ON fi.PortfolioCode = p.acct_id
-- Clear down variable values
SET #PortfolioGroup = NULL
--SET #StartDateTime = NULL
SET #EndDateTime = NULL
-- Clear down #DGControl table to allow new record to be inserted
DELETE FROM #DGControl
-- Move to next record
SET #RcdNum = #RcdNum + 1
END -- END WHILE LOOP
Adding the execution of the stored proc usp_Generic_DGControl at the beginning and then clearing it down after each loop stopped the duplication of the records.