SQL DB script performance tuning - sql

I need to fix a prod DB issue and the clean up script I've is taking really long. I tried couple of things without any luck, following is the script:
DECLARE #ErrorMessage NVARCHAR(4000)
DECLARE #ErrorSeverity INT
DECLARE #ErrorState INT
DECLARE #ErrorProcedure NVARCHAR(50)
BEGIN TRY
IF OBJECT_ID('tempdb..#SuspectData') IS NOT NULL
BEGIN
DROP TABLE #SuspectData
END
CREATE TABLE #SuspectData
(
IID INT,
CID INT,
PID INT
)
INSERT INTO dbo.#SuspectData
SELECT DL.IID,DL.CID,IT.PID FROM DL
INNER JOIN IT ON IT.CID = DL.CID AND IT.IID = DL.IID
WHERE DL.Suspect = 1
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION
UPDATE TOP (5000) TDS
SET TDS.DTID = 4
FROM
TDS
INNER JOIN dbo.#SuspectData SD
ON TDS.IID = SD.IID AND TDS.PID = SD.PID
WHERE TDS.DTID <> 4
IF ##ROWCOUNT = 0
BEGIN
COMMIT TRANSACTION
BREAK
END
COMMIT TRANSACTION
END
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION
UPDATE TOP (5000) TDA
SET TDA.DTID = 4
FROM
TDA
INNER JOIN dbo.#SuspectData SD
ON TDA.IID = SD.IID AND TDA.PID = SD.PID
WHERE TDA.DTID <> 4
IF ##ROWCOUNT = 0
BEGIN
COMMIT TRANSACTION
BREAK
END
COMMIT TRANSACTION
END
DROP TABLE #SuspectData
END TRY
BEGIN CATCH
SELECT #ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorProcedure = ERROR_PROCEDURE()
RAISERROR (#ErrorMessage,#ErrorSeverity,#ErrorState,#ErrorProcedure) ;
END CATCH
I also have following script to update everything at same time but it is also taking really long time like 24 hours or something.
DECLARE #ErrorMessage NVARCHAR(4000)
DECLARE #ErrorSeverity INT
DECLARE #ErrorState INT
DECLARE #ErrorProcedure NVARCHAR(50)
BEGIN TRY
IF OBJECT_ID('tempdb..#SuspectData') IS NOT NULL
BEGIN
DROP TABLE #SuspectData
END
CREATE TABLE #SuspectData
(
IID INT,
CID INT,
PID INT
)
INSERT INTO dbo.#SuspectData
SELECT DL.IID,DL.CID,IT.PID FROM DL
INNER JOIN IT ON IT.CID = DL.CID AND IT.IID = DL.IID
WHERE DL.Suspect = 1
BEGIN TRANSACTION
--Update about 1.5M records
UPDATE TDS
SET TDS.DTID = 4
FROM
TDS
INNER JOIN dbo.#SuspectData SD
ON TDS.IID = SD.IID AND TDS.PID = SD.PID
WHERE TDS.DTID <> 4
COMMIT TRANSACTION
BEGIN TRANSACTION
--Update about 4.5M records
UPDATE TDA
SET TDA.DTID = 4
FROM
TDA
INNER JOIN dbo.#SuspectData SD
ON TDA.IID = SD.IID AND TDA.PID = SD.PID
WHERE TDA.DTID <> 4
COMMIT TRANSACTION
DROP TABLE #SuspectData
END TRY
BEGIN CATCH
SELECT #ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorProcedure = ERROR_PROCEDURE()
RAISERROR (#ErrorMessage,#ErrorSeverity,#ErrorState,#ErrorProcedure) ;
END CATCH

I'm guessing that TDS table is large. In that case you can speed up join operation between your temp table and TDS (ON TDS.IID = SD.IID AND TDS.PID = SD.PID) by creating index on your temporary table:
either primary clustered:
CREATE TABLE #SuspectData
(
IID INT,
CID INT,
PID INT,
CONSTRAINT pk_temp PRIMARY KEY(IID, PID)
)
or not-clustered (if IID-PID pairs are not unique):
CREATE INDEX IDX_Temp_SuspectData ON #SuspectData(IID,PID)
What you can also do is check execution plan of those queries - it will help you locate which operation takes so long.
On the side: I'm generally against using cursors if you can avoid it.

First, is there anything that changes DL.Suspect = 1 to something else? or does your data set just keep getting bigger?
I also agree with Sean Lange, does the update have to be all or nothing?
I would recommend using a cursor. cursors are a great way to break up large transaction to speed up use and reduce table locks.
DECLARE db_cursor CURSOR FOR SELECT DL.IID,DL.CID,IT.PID FROM DL
INNER JOIN IT ON IT.CID = DL.CID AND IT.IID = DL.IID
WHERE DL.Suspect = 1;
DECLARE #first INT;
DECLARE #second INT;
DECLARE #third INT;
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #first , #second , #third ;
WHILE ##FETCH_STATUS = 0
BEGIN
-- Do your updates one row at a time here
UPDATE TDS
SET TDS.DTID = 4
FROM TDS
WHERE TDS.IID = #first AND TDS.PID = #third
WHERE TDS.DTID <> 4
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;

Related

SQL Server : while updating table, how come cannot delete is not updating row

ALTER PROCEDURE [dbo].[BG_CalcNetPT_Loop]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #trancount int;
SET #trancount = ##trancount;
BEGIN TRY
IF #trancount = 0
begin transaction
ELSE
save transaction [BG_CalcNetPT_Loop];
SET NOCOUNT ON;
DECLARE #ErrorMessage NVARCHAR(255) = NULL;
DECLARE #Cursor as CURSOR;
DECLARE #UserId as int
DECLARE #TicketId as nvarchar(50) -- BDBG_IssueId
DECLARE #CreateDate as datetime
BEGIN
SET #Cursor = CURSOR FOR
-- select success bet which not yet calculate PT
select distinct BDBG_IssueId, BDBG_UserId from tbl_BettingDetails_BG a WITH (NOLOCK)
inner join tbl_Pt_BG b WITH (NOLOCK) on a.BDBG_IssueId = b.pt_GameID
where a.BDBG_Status = 1 and b.pt_isCalc = 0 and a.BDBG_Id = b.pt_TransID
and DATEDIFF (DAY, a.BDBG_CreatedDate, GETDATE()) <= 7 and pt_SourceID <> 1104
order by BDBG_IssueId desc
OPEN #Cursor
FETCH NEXT FROM #Cursor INTO
#TicketId
,#UserId
--,#CreateDate
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE
tbl_pt_BG
SET
tbl_pt_BG.pt_UplinePresetPT_or_FixPT = (SELECT CASE
WHEN (pt_fixpt > -1)
THEN pt_fixpt
WHEN (pt_UserType = 3) THEN
(SELECT isnull(BG_PTPercentage,0)
from tbl_UserDetails_BG WITH(NOLOCK) where BG_UserID = #UserID)
ELSE
pt_UplinePresetPT
END),
tbl_pt_BG.pt_downcomm2 = (SELECT CASE
WHEN pt_UserType = 3
THEN BG_RollingCommission
WHEN pt_usertype_lvl1 = 3
THEN BG_RollingCommission
ELSE
pt_downlinecomm
END)
FROM
tbl_pt_BG AS A WITH (NOLOCK)
--LEFT JOIN tbl_MemberFixPTSettingsBG AS B WITH (NOLOCK) ON A.pt_UserUplineID = B.AgentId --save in tbl_pt userid mean this agent take this user fix pt. CY 29/3/2020
LEFT JOIN tbl_UserDetails_BG C WITH(NOLOCK) ON A.pt_userID=c.BG_UserId
WHERE
pt_GameID = #TicketId and pt_SourceID = #UserID and pt_isCalc = 0
FETCH NEXT FROM #Cursor INTO
#TicketId
,#UserId
--,#CreateDate
END;
CLOSE #Cursor ;
DEALLOCATE #Cursor;
END;
lbexit:
if #trancount = 0
commit;
end try
begin catch
declare #error int, #message varchar(4000), #xstate int;
select #error = ERROR_NUMBER(),
#message = ERROR_MESSAGE(),
#xstate = XACT_STATE();
if #xstate = -1
rollback;
if #xstate = 1 and #trancount = 0
rollback
if #xstate = 1 and #trancount > 0
rollback transaction [BG_CalcNetPT_Loop];
SELECT CASE WHEN #ErrorMessage IS NULL THEN 'error_unknown_error' ELSE #ErrorMessage END AS errorMessage
--SELECT CASE WHEN #ErrorMessage IS NULL THEN ERROR_MESSAGE() ELSE #ErrorMessage END AS errorMessage
INSERT INTO tbl_debug
(
debug_sp,
debug_ticketid,
debug_text,
debug_createddate
)
VALUES
(
'API_BG_Order_Transfer',
CAST(#TicketId AS NVARCHAR(50)),
CAST(ERROR_MESSAGE() AS NVARCHAR(MAX)),
GETDATE()
)
raiserror ('API_BG_Order_Transfer: %d: %s', 16, 1, #error, #message) ;
end catch
END;
This code is a loop to update tbl_pt_dg where pt_sourceid <> 1104 while this loop is executing, I'm trying to run this delete query
delete from tbl_Pt_BG
where pt_GameID = '123'
and pt_isCalc = 1
and pt_SourceID = 1104
But this returns an error
Transaction (Process ID 70) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Why while executing updating tbl_pt_bg is it locking the whole table? I can't wait for complete updating only delete the data.
Cursors and loops should be avoiding because they are terrible slow. You should try thinking in set operations instead of loops. It's not surprising that you get this error, until the UPDATE from inside the CURSOR finishes, the DELETE is blocked. All that work is wrapped inside a BEGIN TRANSACTION, until the COMMIT is done, the DELETE will be blocked.

sql linked server Insert statement fails inside the cursor

Insert statement fails inside the cursor when I try to insert values in to SQL linked server.
If I run the same insert statement outside cursor then it works fine. Is there any settings to be done while creating linked server?
Error message:
OLE DB provider "SQL...." for linked server "" returned message "The parameter is incorrect.".
This is my Code Linked Server in Cursor
SET NOCOUNT ON;
DECLARE #SUBCATEGORY_NAME AS VARCHAR(100), #CategoryStatus AS BIT, #BRAND_NAME AS VARCHAR(100), #BrandMasterStatus AS BIT, #BrandManfacturerName AS VARCHAR(MAX), #PRODUCT_NAME VARCHAR(100), #ProductStatus AS BIT,
#ProductIsReturnable AS BIT, #PRINT_ON_RECEIPT AS VARCHAR(40), #TenantId INT, #CreationTime DATETIME2, #ParentId INT, #BAR_CODE_NO AS VARCHAR(40),
#TAX_CODE AS VARCHAR(MAX), #TaxName AS VARCHAR(MAX), #TaxInclusive AS BIT, #TAX_PERCENTAGE AS FLOAT, #TaxStartDateTime DATETIME2, #BUSINESS_TYPE VARCHAR(20);
--PRINT '-------- Product table migration --------';
DECLARE Product_Cursor CURSOR FOR
SELECT
SC.SUBCATEGORY_NAME,CASE WHEN SC.DEFUNCT_IND = 'N' THEN 1 ELSE 0 END AS CategoryStatus,
BM.BRAND_NAME,CASE WHEN BM.DEFUNCT_IND = 'N' THEN 1 ELSE 0 END AS BrandMasterStatus,BM.MANUFACTURER AS BrandManfacturerName,
P.PRODUCT_NAME,CASE WHEN P.DEFUNCT_IND = 'N' THEN 1 ELSE 0 END AS ProductStatus,
CASE WHEN P.RETURNABLE = 'Y' THEN 1 ELSE 0 END AS ProductIsReturnable,
P.PRINT_ON_RECEIPT,
--SubCategoryId,
--BrandId,
--1 AS TaxId,
1 AS TenantId,
GETDATE() AS CreationTime,
0 AS ParentId,
P.BAR_CODE_NO,
TS.TAX_CODE,
TS.TAX_DESC AS TaxName,
CASE WHEN TS.TAX_INCLUDED = '1' THEN 1 ELSE 0 END AS TaxInclusive,
TS.TAX_PERCENTAGE,
TS.EFFECTIVE_DATE AS TaxStartDateTime,
P.BUSINESS_TYPE
FROM CISPROD.dbo.PRODUCT AS P
RIGHT OUTER JOIN CISPROD.dbo.SUBCATEGORY AS SC ON SC.SUBCATEGORY_ID = P.SUBCATEGORY_ID
FULL OUTER JOIN CISPROD.dbo.BRANDMSTR AS BM ON BM.BRANDMSTR_ID = P.BRANDMSTR_ID
FULL OUTER JOIN CISPROD.dbo.TAX_SETUP AS TS ON TS.TAX_CODE = P.TAX_GROUP
OPEN Product_Cursor
FETCH NEXT FROM Product_Cursor
INTO #SUBCATEGORY_NAME,#CategoryStatus,#BRAND_NAME, #BrandMasterStatus, #BrandManfacturerName, #PRODUCT_NAME, #ProductStatus, #ProductIsReturnable,
#PRINT_ON_RECEIPT, #TenantId, #CreationTime,#ParentId,#BAR_CODE_NO,#TAX_CODE, #TaxName, #TaxInclusive, #TAX_PERCENTAGE, #TaxStartDateTime, #BUSINESS_TYPE;
BEGIN TRANSACTION
BEGIN TRY
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT ' '
--DECLARE #message VARCHAR(MAX)
--SELECT #message = '----- Products CISPROD: ' + #PRODUCT_NAME
--PRINT #message
--Insert Product Categories Table
DECLARE #CategoryId INT
IF ISNULL(#SUBCATEGORY_NAME,'') <>''
BEGIN
---IF NOT EXISTS(SELECT [Name] FROM ProductCategories WHERE ISNULL([Name], '') = ISNULL(#SUBCATEGORY_NAME,''))
IF NOT EXISTS(SELECT [Name] FROM [AZUREDATABASE].[sds-pos-storiveo-db].dbo.ProductCategories WHERE [Name] = #SUBCATEGORY_NAME)
BEGIN
INSERT INTO [AZUREDATABASE].[sds-pos-storiveo-db].dbo.ProductCategories([Name],CreationTime,Inactive,ParentId,TenantId) VALUES(#SUBCATEGORY_NAME,#CreationTime,#CategoryStatus,#ParentId,#TenantId)
SELECT #CategoryId = ##IDENTITY
END
ELSE
BEGIN
SELECT #CategoryId = Id FROM [AZUREDATABASE].[sds-pos-storiveo-db].dbo.ProductCategories WHERE [Name] = #SUBCATEGORY_NAME
END
END
--Insert Product Brand Table
DECLARE #BrandId INT
--DECLARE #DefaultBrandId INT
--SET #DefaultBrandId = 1
IF ISNULL(#BRAND_NAME,'') <>''
BEGIN
--IF NOT EXISTS(SELECT BrandName FROM ProductBrands WHERE ISNULL(BrandName,'') = ISNULL(#BRAND_NAME,''))
IF NOT EXISTS(SELECT BrandName FROM [AZUREDATABASE].[sds-pos-storiveo-db].dbo.ProductBrands WHERE BrandName = #BRAND_NAME)
BEGIN
INSERT INTO [AZUREDATABASE].[sds-pos-storiveo-db].dbo.ProductBrands(BrandName,ManufacturerName,CreationTime,Inactive) VALUES(#BRAND_NAME,#BrandManfacturerName,#CreationTime,#BrandMasterStatus)
SELECT #BrandId = ##IDENTITY
END
ELSE
BEGIN
SELECT #BrandId = Id FROM [AZUREDATABASE].[sds-pos-storiveo-db].dbo.ProductBrands WHERE BrandName = #BRAND_NAME
END
END
--ELSE
--BEGIN
-- SET #BrandId = #DefaultBrandId
--END
--Insert Tax Table Records
DECLARE #TaxId INT
IF ISNULL(#TAX_CODE,'') <>''
BEGIN
IF NOT EXISTS(SELECT Code FROM [AZUREDATABASE].[sds-pos-storiveo-db].dbo.Taxes WHERE Code = #TAX_CODE)
BEGIN
INSERT INTO [AZUREDATABASE].[sds-pos-storiveo-db].dbo.Taxes(Code,Inactive,IsInclusive,[Name],TaxTypeId,TenantId) VALUES(#TAX_CODE,1,#TaxInclusive,#TaxName,1,#TenantId)
SELECT #TaxId = ##IDENTITY
INSERT INTO [AZUREDATABASE].[sds-pos-storiveo-db].dbo.TaxSchedules([Percentage],StartDateTime,TaxId) VALUES(#TAX_PERCENTAGE,#TaxStartDateTime,#TaxId)
END
ELSE
BEGIN
SELECT #TaxId = Id FROM [AZUREDATABASE].[sds-pos-storiveo-db].dbo.Taxes WHERE Code = #TAX_CODE
END
END
--Insert Product Table Records
DECLARE #ProductId INT
IF ISNULL(#PRODUCT_NAME,'') <>''
BEGIN
IF (#BUSINESS_TYPE = 'NF')
BEGIN
INSERT INTO [AZUREDATABASE].[sds-pos-storiveo-db].dbo.Products([Name],Inactive,IsReturnable,PrintName,
ProductBrandId,ProductCategoryId,TaxId,TenantId,CreationTime)
VALUES(#PRODUCT_NAME,#ProductStatus,#ProductIsReturnable,#PRINT_ON_RECEIPT,#BrandId,#CategoryId,#TaxId,#TenantId,#CreationTime)
END
ELSE
BEGIN
INSERT INTO [AZUREDATABASE].[sds-pos-storiveo-db].dbo.FuelProducts([Name],Inactive,PrintName,
ProductCategoryId,TaxId,TenantId,CreationTime)
VALUES(#PRODUCT_NAME,#ProductStatus,#PRINT_ON_RECEIPT,#CategoryId,#TaxId,#TenantId,#CreationTime)
END
SELECT #ProductId = ##IDENTITY
END
--Insert Product Barcode Records
IF ISNULL(#BAR_CODE_NO,'') <>''
BEGIN
IF (#BUSINESS_TYPE = 'NF')
BEGIN
INSERT INTO [AZUREDATABASE].[sds-pos-storiveo-db].dbo.ProductBarcodes(Barcode,Inactive,ProductId)VALUES(#BAR_CODE_NO,#ProductStatus,#ProductId)
END
END
FETCH NEXT FROM Product_Cursor
INTO #SUBCATEGORY_NAME,#CategoryStatus,#BRAND_NAME, #BrandMasterStatus, #BrandManfacturerName, #PRODUCT_NAME, #ProductStatus, #ProductIsReturnable,
#PRINT_ON_RECEIPT, #TenantId, #CreationTime, #ParentId, #BAR_CODE_NO, #TAX_CODE, #TaxName, #TaxInclusive, #TAX_PERCENTAGE, #TaxStartDateTime,#BUSINESS_TYPE
END
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
-- any other logiing or cleanup
END CATCH
IF ##TranCount>0 -- Transaction still open, so must have succeeded. If rolled back, trancount would be 0
COMMIT TRANSACTION
CLOSE Product_Cursor;
DEALLOCATE Product_Cursor;

RAISERROR Dosn't Work Inside CATCH With ROLLBACK TRANSACTION

I created a Stored Procedure to Insert Into 2 Table With Transaction to make sure That Both Inserts Done and I Used TRY and CATCH to Handle The Errors .. The Problem Is In The Catch Statement I Put ROLLBACK TRANS and RAISERROR The RoLLBACK Works But The Procedure Dose not RAISERROR
Here is The Code
ALTER PROC SP_InsertPlot
#PlotName nvarchar(50),
#GrossArea int,
#SectorName Nvarchar(50),
#PlotYear int,
#OwnerName Nvarchar(50),
#Remarks text,
#NumberOfPlants INT,
#NetArea INT,
#Category Nvarchar(50),
#Type Nvarchar(50),
#Variety Nvarchar(50),
#RootStock Nvarchar(50),
#PlantDistance Decimal(18,2)
AS
BEGIN
DECLARE #PlotID INT
SET #PlotID = (SELECT ISNULL(MAX(PlotID),0) FROM Plots) + 1
DECLARE #SectorID INT
SET #SectorID = (SELECT SectorID FROM Sectors WHERE SectorName = #SectorName)
DECLARE #OwnerID INT
SET #OwnerID = ( SELECT OwnerID FROM Owners WHERE OwnerName = #OwnerName)
DECLARE #CategoryID INT
SET #CategoryID = (SELECT CategoryID FROM Categories WHERE CategoryName = #Category)
DECLARE #TypeID INT
SET #TypeID = (SELECT TypeID FROM Types WHERE TypeName = #Type)
DECLARE #VarietyID INT
SET #VarietyID = (SELECT VarietyID FROM Varieties WHERE VarietyName = #Variety)
DECLARE #RootStockID INT
SET #RootStockID = (SELECT RootStockID FROM RootStocks WHERE RootStockName = #RootStock)
DECLARE #PlotDescID INT
SET #PlotDescID = (SELECT ISNULL(MAX(PlotDescID),0) FROM PlotDescriptionByYear) + 1
BEGIN TRY
SET XACT_ABORT ON
SET NOCOUNT ON
IF(SELECT Count(*) FROM Plots WHERE PlotName = #PlotName) = 0
BEGIN
BEGIN TRANSACTION
INSERT INTO Plots (PlotID,PlotName,GrossArea,SectorID,PlantYear,OnwerID,Remarks)
VALUES(#PlotID,#PlotName,#GrossArea,#SectorID,#PlotYear,#OwnerID,#Remarks)
INSERT INTO PlotDescriptionByYear (PlotDescID, PlantYear, NumberOfPlants,PlotID,NetArea,CategoryID,TypeID,VarietyID,RootStockID,PlantDistance)
VALUES(#PlotDescID,YEAR(GETDATE()),#NumberOfPlants,#PlotID -1,#NetArea,#CategoryID,#TypeID,#VarietyID,#RootStockID,#PlantDistance)
COMMIT TRANSACTION
END
END TRY
BEGIN CATCH
IF(XACT_STATE())= -1
BEGIN
ROLLBACK TRANSACTION
RAISERROR('This Plot Is Already Exists !!',11,1)
END
END CATCH
END
By the way i tried to change the Severity and I tried ##TRANCOUNT instead of XACT_STATE and the Same Problem Happens Which is When I Exec Proc and Pass an Existing Data To The Parameters The Transaction Roll back and did not rise the error
Change IF(XACT_STATE())= -1 to IF(XACT_STATE()) <> 0 and your problem will be done.

Execute SP as many times as rows in a temp table?

In my SP I have TVP type which include product information like,
Name Desc Visible Tags
This TVP include n rows. I am inserting this n rows in my Products table and putting the inserted Ids with Tags column in a temp table. After inserting, I need to execute my SP for each of the inserted product,
EXEC [InsertOrUpdateTags]
Means I need to execute this SP as many times as the number of rows in the temp table and passing the inserted Id. How can I do this? Here is my SP
ALTER PROCEDURE [dbo].[InsertOrUpdateTags]
(
#ProductId INT
,#Tags NVARCHAR(225)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TranCount INT;
SET #TranCount = ##TRANCOUNT;
BEGIN TRY
IF #TranCount = 0
BEGIN TRANSACTION
ELSE
SAVE TRANSACTION InsertOrUpdateTags;
DECLARE #Tag TABLE(Name NVARCHAR(50));
DECLARE #TagIds TABLE(Id INT)
INSERT INTO #Tag
SELECT Items FROM dbo.Split(#Tags,',');
MERGE Tags AS D
USING (SELECT Name FROM #Tag) S
ON D.Name = S.Name
WHEN NOT MATCHED THEN
INSERT(Name)
VALUES(S.Name)
WHEN MATCHED THEN
UPDATE
SET Name = S.Name
OUTPUT INSERTED.ID INTO #TagIds;
-- Delete the one which was available before but not now
DELETE FROM ProductsTags WHERE BaseProductId = #ProductId AND TagId NOT IN (SELECT Id FROM #TagIds);
MERGE ProductsTags AS D
USING (SELECT Id FROM #TagIds) S
ON D.TagId = S.Id AND D.BaseProductId = #ProductId
WHEN NOT MATCHED THEN
INSERT(BaseProductId, TagId)
VALUES(#ProductId, S.Id);
LBEXIT:
IF #TranCount = 0
COMMIT;
END TRY
BEGIN CATCH
DECLARE #Error INT, #Message VARCHAR(4000), #XState INT;
SELECT #Error = ERROR_NUMBER() ,#Message = ERROR_MESSAGE() ,#XState = XACT_STATE();
IF #XState = -1
ROLLBACK;
IF #XState = 1 AND #TranCount = 0
rollback
IF #XState = 1 AND #TranCount > 0
ROLLBACK TRANSACTION InsertOrUpdateTags;
RAISERROR (' InsertOrUpdateTags: %d: %s', 16, 1, #error, #message) ;
END CATCH
END
You can do a CROSS APPLY with your Split to get all list of products with tag in a table variable and use the table variable in all your merge statements.
Something like this.
DECLARE #TempProduct TABLE(ProductID INTTagName VARCHAR(100))
INSERT INTO #TempProduct(ProductID,TagName)
SELECT ProductID,S.Items
FROM #TempTable CROSS APPLY dbo.Split(Tags,',') S;
MERGE Tags AS D
USING (SELECT DISTINCT TagName FROM #TempProduct) S
ON D.Name = S.Name
WHEN NOT MATCHED THEN
INSERT(Name)
VALUES(S.Name)
WHEN MATCHED THEN
UPDATE
SET Name = S.Name
OUTPUT INSERTED.ID,Inserted.Name INTO #TagIds(ID,Name);
-- Delete the one which was available before but not now
DELETE FROM ProductsTags
WHERE NOT EXISTS (SELECT 1 FROM #TempProduct TP INNER JOIN #TagIds TS ON TP.TagName = TS.TagName WHERE TP.ProductID = BaseProductId AND TS.ID= ProductsTags.TagId);
MERGE ProductsTags AS D
USING (SELECT TP.ProductID,TS.ID FROM #TempProduct TP INNER JOIN #TagIds TS ON TP.TagName = TS.TagName) S
ON D.TagId = S.ID AND D.BaseProductId = ProductID
WHEN NOT MATCHED THEN
INSERT(BaseProductId, TagId)
VALUES(ProductID, TagID);
LBEXIT:
IF #TranCount = 0
COMMIT;
END TRY
BEGIN CATCH
DECLARE #Error INT, #Message VARCHAR(4000), #XState INT;
SELECT #Error = ERROR_NUMBER() ,#Message = ERROR_MESSAGE() ,#XState = XACT_STATE();
IF #XState = -1
ROLLBACK;
IF #XState = 1 AND #TranCount = 0
rollback
IF #XState = 1 AND #TranCount > 0
ROLLBACK TRANSACTION InsertOrUpdateTags;
RAISERROR (' InsertOrUpdateTags: %d: %s', 16, 1, #error, #message) ;
END CATCH
END
Note: this was written here directly and might have some issues.
Hope this helps.

Merge sql with date condition

I have create merge stored procedures as below, what i am trying to achieve is the following scenario:
Merge the new record if ProductTRN is not exist in ProductList table (complete)
Only Update the ProductList record in where the PU.CreateDate is bigger than CreateDate of target table which is ProductList (Not Complete)
Please advise me how I can achieve the second scenario above, thank you
CREATE PROCEDURE [dbo].[usp_ProductList_Merge]
AS
BEGIN
DECLARE #retValue INT
BEGIN TRY
IF OBJECT_ID('ProductList') IS NOT NULL
BEGIN
BEGIN TRANSACTION MergeConsumerTable
SET NOCOUNT ON;
MERGE dbo.ProductList AS target
USING
( SELECT
PU.ProductTRN,
PU.ProductName,
PU.ProductDescription,
PU.CreateDate
FROM dbo.TmpProductList PU
WHERE PU.ProductTRN = ProductTRN
) AS source (
ProductTRN,
ProductName,
ProductDescription
CreateDate)
ON ( (target.ProductTRN) = LOWER(source.ProductTRN)
)
WHEN MATCHED
THEN
UPDATE SET
ProductTRN= source.ProductTRN
WHEN NOT MATCHED
THEN
INSERT (
ProductTRN,
ProductName,
ProductDescription,
CreateDate
) VALUES
(
source.ProductTRN,
source.ProductName,
source.ProductDescription,
source.CreateDate,
);
DELETE PU
FROM dbo.TmpProductList PU
COMMIT TRANSACTION MergeProductListTable
SET #retValue = 1
SELECT #retValue
END
ELSE
BEGIN
SET #retValue = -1
SELECT #retValue
END
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION MergeProductListTable
DECLARE #ErrorMsg VARCHAR(MAX);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SET #ErrorMsg = ERROR_MESSAGE();
SET #ErrorSeverity = ERROR_SEVERITY();
SET #ErrorState = ERROR_STATE();
SET #retValue = 0
SELECT #retValue
-- SELECT 0 AS isSuccess
END CATCH
END
WITH Source AS (
SELECT ProductTRN
,ProductName
,ProductDescription
,CreateDate
FROM dbo.TmpProductList
)
MERGE ProductList AS Target
USING Source
ON Target.ProductTRN = Source.ProductTRN
WHEN MATCHED
AND Source.CreatedDate > Target.CreatedDate
THEN UPDATE SET
ProductName = Source.ProductName
,ProductDescription = Source.ProductDescription
,CreateDate = Source.CreatedDate
WHEN NOT MATCHED BY TARGET
THEN INSERT (
ProductTRN
,ProductName
,ProductDescription
,CreateDate
)
VALUES (
Source.ProductTRN
,Source.ProductName
,Source.ProductDescription
,Source.CreatedDate
)