Facing an issue in MERGE SQL query - sql-server
I have a requirement, insert/update data from staging table stgTbl to another table T2. If exists update, no matter how many duplicates are there. Similarly, if not exists insert directly T2. Very Simple. Since the staging table stgTb1 is scheduled job everyday. Sometime I would get multiple duplicate rows as well. So because of duplicates, merge statement producing an error:
"The MERGE statement attempted to UPDATE or DELETE the same row more
than once. This happens when a target row matches more than one source
row".
I tried with checksum but still getting because i may done wrongly in checksum. There is no primary key in both the tables (i removed, because otherwise i am getting primary key constraint error). Based on columns (account and ref_key2) should go with insert/update. I have tried with if else with exists also but somehow this is also not working if in case there are 0 records in the target table T2. SQL masters could solve this issue. Appreciate their knowledge share.
CREATE OR ALTER Trigger [dbo].[tr_Merge_Personal_Expense] on [dbo].
[Personal_Expense_Staging]
AFTER INSERT
AS
BEGIN
DECLARE #SummaryOfChanges TABLE(Change nvarchar(20));
MERGE [dbo].[Personal_Expense] AS TARGET
USING (
SELECT *,
CHECKSUM(
ISNULL(CONVERT(nvarchar (3) ,[Client]),'0'),
ISNULL(CONVERT(nvarchar (50),[Text]),'0'),
ISNULL(CONVERT(nvarchar (10),[Cost_Center]),'0'),
ISNULL(CONVERT(nvarchar (2) ,[Posting_Period]),'0'),
ISNULL(CONVERT(nvarchar (10),[Profit]),'0') ,
ISNULL(CONVERT(nvarchar (2) ,[Document_Type]),'0'),
ISNULL(CONVERT(nvarchar (4) ,[Company_Code]),'0') ,
-- ISNULL(CONVERT(nvarchar (10),[Account]),'0'),
ISNULL(CONVERT(nvarchar (20),[Amount_In_Doc_Curr]),'0'),
ISNULL(CONVERT(nvarchar (5) ,[Document_Currency]),'0') ,
ISNULL(CONVERT(nvarchar (20),[Amount_In_local_Curr]),'0') ,
ISNULL(CONVERT(nvarchar (20),[Amount_In_Grp_Curr]),'0'),
ISNULL(CONVERT(nvarchar (25),[Group]),'0'),
[Posting_Date],
ISNULL(CONVERT(nvarchar (18), [Assignment]),'0'),
ISNULL(CONVERT(nvarchar (16), [Reference]),'0'),
ISNULL(CONVERT(nvarchar (20), [Reference_Key1]),'0') ,
--[Reference_Key2] [nvarchar](12) NOT NULL,
ISNULL(CONVERT(nvarchar (20), [Reference_Key3]),'0') ,
ISNULL(CONVERT(nvarchar (20), [Document_Number]),'0') ,
ISNULL(CONVERT(nvarchar (25), [Document_Header_Text]),'0')
) AS [HashChecksum]
FROM
[dbo].[Personal_Expense_Staging]
) AS SOURCE
ON (
TARGET.[Reference_Key2] = SOURCE.[Reference_Key2] AND
TARGET.[Account] = SOURCE.[Account]
)
----- Update
WHEN MATCHED AND ( TARGET.[HashChecksum] <> SOURCE.[HashChecksum] )
THEN
UPDATE SET
TARGET.[client] = SOURCE.[client],
TARGET.[text] = SOURCE.[text],
TARGET.[cost_center] = SOURCE.[cost_center],
TARGET.[posting_period] = SOURCE.[posting_period],
TARGET.[profit] = SOURCE.[profit],
TARGET.[document_type]= SOURCE.[document_type],
TARGET.[company_code] = SOURCE.[company_code],
TARGET.[Account] = SOURCE.[Account],
TARGET.[Amount_In_Doc_Curr] = SOURCE.[Amount_In_Doc_Curr],
TARGET.[document_currency] = SOURCE.[document_currency],
TARGET.[Amount_In_local_Curr] = SOURCE.[Amount_In_local_Curr],
TARGET.[Amount_In_Grp_Curr] = SOURCE.[Amount_In_Grp_Curr],
TARGET.[group] = SOURCE.[group],
TARGET.[posting_date] = SOURCE.[posting_date],
TARGET.[assignment] = SOURCE.[assignment],
TARGET.[reference] = SOURCE.[reference],
TARGET.[document_header_text] = SOURCE.[document_header_text],
TARGET.[Last_updated_DateTime] = GETDATE(),
TARGET.[HashChecksum] = SOURCE.[HashChecksum]
-- Insert
WHEN NOT MATCHED THEN
INSERT (
[Client],
[text],
[cost_center],
[posting_period],
[profit],
[document_type],
[company_code],
[Account],
[Amount_In_Doc_Curr],
[document_currency],
[Amount_In_local_Curr],
[Amount_In_Grp_Curr],
[group],
[posting_date],
[assignment],
[reference],
[reference_key2],
[reference_key3],
[document_number],
[document_header_text],
[Last_updated_DateTime],
[HashChecksum]
)
VALUES (
SOURCE.[client],
SOURCE.[text],
SOURCE.[cost_center],
SOURCE.[posting_period],
SOURCE.[profit],
SOURCE.[document_type],
SOURCE.[company_code],
SOURCE.[Account],
SOURCE.[Amount_In_Doc_Curr],
SOURCE.[document_currency],
SOURCE.[Amount_In_local_Curr],
SOURCE.[Amount_In_Grp_Curr],
SOURCE.[group],
SOURCE.[posting_date],
SOURCE.[assignment],
SOURCE.[reference],
SOURCE.[reference_key2],
SOURCE.[reference_key3],
SOURCE.[document_number],
SOURCE.[document_header_text],
GETDATE(),
SOURCE.[HashChecksum]
)
OUTPUT $action INTO #SummaryOfChanges;
DECLARE #RowsProcessed INT = 0;
SELECT
#RowsProcessed = ISNULL([INSERT],0) + ISNULL([UPDATE],0) + ISNULL([DELETE],0)
FROM (
SELECT COUNT(*) ChangeCount, Change as ChangeType
FROM #SummaryOfChanges
GROUP BY Change
)Main
PIVOT (
MAX(ChangeCount)
FOR ChangeType IN ([INSERT],[UPDATE],[DELETE])
)Pvt;
SELECT #RowsProcessed AS RowsProcessed;
CREATE OR ALTER TRIGGER [dbo].[tr_merge_personal_expense]
ON personal_expense_staging
AFTER INSERT AS
----- CASE 1: IF (ACCOUNT AND REF_KEY2) ARE MATCHED THEN UPDATE
IF EXISTS
(
SELECT
*
FROM dbo.personal_expense p
INNER JOIN inserted e
ON p.[account] = e.[account] AND p.[reference_key2] = e.
[reference_key2]
)
BEGIN
UPDATE target
SET
target.[document_number] = source.[document_number],
target.[client] = source.[client],
target.[text] = source.[text],
target.[cost_center] = source.[cost_center],
target.[posting_period] = source.[posting_period],
target.[profit] = source.[profit],
target.[document_type] = source.[document_type],
target.[company_code] = source.[company_code],
target.[amount_in_doc_curr] = source.[amount_in_doc_curr],
target.[document_currency] = source.[document_currency],
target.[amount_in_local_curr] = source.[amount_in_local_curr],
target.[amount_in_grp_curr] = source.[amount_in_grp_curr],
target.[group] = source.[group],
target.[posting_date] = source.[posting_date],
target.[assignment] = source.[assignment],
target.[reference] = source.[reference],
target.[reference_key1] = source.[reference_key1],
target.[reference_key3] = source.[reference_key3],
target.[document_header_text] = source.[document_header_text],
target.[last_updated_datetime] = GETDATE()
FROM personal_expense target
INNER JOIN inserted source
ON target.[account] = source.[account]
AND target.[reference_key2] = source.[reference_key2];
END;
------ CASE 2: IF (ACCOUNT AND REF_KEY2) ARE NOT MATCHED THEN INSERT
IF NOT EXISTS
(
SELECT
*
FROM dbo.personal_expense p
INNER JOIN inserted e
ON p.[account] = e.[account] AND p.[reference_key2] = e.
[reference_key2]
)
BEGIN
INSERT INTO dbo.personal_expense
(
[client],
[text],
[cost_center],
[posting_period],
[profit],
[document_type],
[company_code],
[account],
[amount_in_doc_curr],
[document_currency],
[amount_in_local_curr],
[amount_in_grp_curr],
[group],
[posting_date],
[assignment],
[reference],
[reference_key1],
[reference_key2],
[reference_key3],
[document_number],
[document_header_text],
[last_updated_datetime]
)
SELECT
source.[client],
source.[text],
source.[cost_center],
source.[posting_period],
source.[profit],
source.[document_type],
source.[company_code],
source.[account],
source.[amount_in_doc_curr],
source.[document_currency],
source.[amount_in_local_curr],
source.[amount_in_grp_curr],
source.[group],
source.[posting_date],
source.[assignment],
source.[reference],
source.[reference_key1],
source.[reference_key2],
source.[reference_key3],
source.[document_number],
source.[document_header_text],
GETDATE()
FROM inserted source;
END;
SO, you know that of duplicates, merge statement producing an error. Then simply remove the duplicates in your source clause, using DISTINCT:
SELECT DISTINCT *
FROM [dbo].[Personal_Expense_Staging]
Related
Snowflake Merge using streams
Merge statement throws: "Boolean value is not recognized" I'm reading all varchar values from streams and writing to a master table. I dont have any Boolean in the source or destination table. Unable to find out why I'm getting: "Boolean value is not recognized" Error." create table "EMP_TEST" (EMPID integer, EMPNAME VARCHAR(500), EMPADD VARCHAR(500), EMPSALARY INTEGER); create table "EMP_TEST_MAIN" (EMPID integer, EMPNAME VARCHAR(500), EMPADD VARCHAR(500), EMPSALARY INTEGER); create or replace stream ST_EMP_TEST on table "EMP_TEST"; insert into "EMP_TEST" select 1, 'AAA','PLACE 1', 100 UNION select 2, 'BBB','PLACE 2', 200 UNION select 3, 'CCC','PLACE 3', 300; MERGE INTO "EMP_TEST_MAIN" AS T USING (select * from ST_EMP_TEST where NOT (METADATA$ACTION ='DELETE' AND METADATA$ISUPDATE = TRUE)) AS S ON T.EMPID = S.EMPID WHEN MATCHED AND S.METADATA$ACTION = 'INSERT' AND S.METADATA$ISUPDATE THEN UPDATE SET T.EMPNAME = S.EMPNAME AND T.EMPADD = S.EMPADD AND T.EMPSALARY = S.EMPSALARY WHEN MATCHED AND S.METADATA$ACTION = 'DELETE' THEN DELETE WHEN NOT MATCHED AND S.METADATA$ACTION = 'INSERT' THEN INSERT (T.EMPID, T.EMPNAME, T.EMPADD, T.EMPSALARY) VALUES (S.EMPID, S.EMPNAME, S.EMPADD, S.EMPSALARY);
The columns in UPDATE part are separated with AND: WHEN MATCHED AND S.METADATA$ACTION = 'INSERT' AND S.METADATA$ISUPDATE THEN UPDATE SET T.EMPNAME = S.EMPNAME AND T.EMPADD = S.EMPADD AND T.EMPSALARY = S.EMPSALARY -- AND is incorrect in this context Should be ,: WHEN MATCHED AND S.METADATA$ACTION = 'INSERT' AND S.METADATA$ISUPDATE THEN UPDATE SET T.EMPNAME = S.EMPNAME ,T.EMPADD = S.EMPADD ,T.EMPSALARY = S.EMPSALARY
Subquery returned more than 1 value when trigger executes
I have a trigger which adds a log entry into a table upon a field change in another table. it works when one row is changed but errors when multiple rows re changed. Anyone out there able to explain what I have to do to get my trigger working also for multi row updates? Many thanks, Derek Declare #PropertyID uniqueidentifier Set #PropertyID = (Select CONVERT(VARCHAR( 36 ), ISNULL(i.[PropertyPK], d.[PropertyPK])) FROM INSERTED i FULL OUTER JOIN DELETED d ON ( d.[PropertyPK] = i.[PropertyPK] ) WHERE ( d.[strManagingOfficeName] <> i.[strManagingOfficeName] ) OR ( d.[strManagingOfficeName] IS NULL AND i.[strManagingOfficeName] IS NOT NULL ) OR ( i.[strManagingOfficeName] IS NULL AND d.[strManagingOfficeName] IS NOT NULL )) Declare #CompanyID uniqueidentifier Set #CompanyID = (Select CompanyFK From Property Where PropertyPK = #PropertyID) --Deleted Old Ones Delete From TDSAPILog Where ObjectFK = #PropertyID And strObject = 'Branch Change' --Insert New Log INSERT dbo.TDSAPILog(TDSAPILogPK, ObjectFK, strObject, strStatus, CompanyFK, dteDateLogged) SELECT NewID(), #PropertyID, 'Branch Change', 'Active', #CompanyID , GetDate()
This error occur when you return more than 1 value from a query and save in a variable or compare with a value in where clause. In your example I think the error occur at this line SET #CompanyID = (SELECT CompanyFK FROM Property WHERE PropertyPK = #PropertyID) To resolve the reported error just put "TOP 1" in your query. Example is shown here: SET #CompanyID = (SELECT TOP 1 CompanyFK FROM Property WHERE PropertyPK = #PropertyID) Subquery returned more than 1 value error may occur at the following scenarios: SET #YouVariable = (SELECT ColumnID FROM yourTable WHERE Identity = #SomeValue) -- if the above query return more than 1 value the same error will be occurred -- to resolve this problem just put "TOP 1" before ColumnID SELECT * FROM OtherTable WHERE OtherIdentity = ((SELECT ColumnID FROM yourTable WHERE Identity = #SomeValue)) -- if the above query return more than 1 value the same error will be occurred -- to resolve this problem just replace "= with IN()". Example give below SELECT * FROM OtherTable WHERE OtherIdentity IN ((SELECT ColumnID FROM yourTable WHERE Identity = #SomeValue))
if Condition for this query
How to do if condition for this query....if record not exist then insert else update from this select SELECT CAST(left(TerminalName, patindex('%[^0-9]%', TerminalName+'.') - 1) as int) ,'04944700' ,SUBSTRING(TerminalName , CHARINDEX('-' , TerminalName) + 1, LEN(TerminalName)) ,TerminalName ,'Bulk' ,'0010' ,user ,GETDATE() ,user ,GETDATE() FROM External_Blk_Itm_Contracts WHERE TerminalName NOT IN (SELECT MBFTERMINALNAME FROM budterminals)
What you want to use is the MERGE statement. Your Select statement will be the your source of data, and then you will insert or update depending on whether a row matching the join condition exists. Here is an example from T-SQL I wrote: MERGE ODS.dbo.Mytable as [Target] USING [Source Query] as [Source] ON [Target].SubId = [Source].SubId And [Target].WorkflowId = [Source].WorkflowId WHEN NOT MATCHED BY TARGET THEN INSERT ( [SubId] ,[WorkflowId] ,[ReadTime] ,[ReadTimeLocal] ) VALUES ( [SubId] ,[WorkflowId] ,[ReadTime] ,[ReadTimeLocal] ) WHEN MATCHED THEN UPDATE Set [Target].ReadTime = [Source].ReadTime, [Target].ReadTimeLocal = [Source].ReadTimeLocal, ;
IF statement in SQL Server stored procedure
I have the following stored procedure: ALTER PROCEDURE [dbo].[sp_Detail] #ReceiptNumber int AS BEGIN SET NOCOUNT ON; WITH invoiceT AS (SELECT fs_transaksie.enti AS Entity, fs_transaksie.rek AS Account, fs_transaksie.trans_tipe AS TransactionType, fs_transaksie.verwysnr AS ReferenceNumber FROM mf_history.dbo.fs_transaksie WHERE ( fs_transaksie.verwysnr = #ReceiptNumber ) AND (fs_transaksie.trans_tipe = 3)) , transactionT as (SELECT fs_kwitansie.kwitansienr AS InvoiceNumber, fs_kwitansie.ktkaart_nr AS CreditCardNumber, fs_kwitansie.ktkaart_bank AS CCBank, fs_kwitansie.ktkaart_bedrag AS CCAmount FROM mf_history.dbo.fs_kwitansie WHERE ( fs_kwitansie.kwitansienr = #ReceiptNumber ) ) select * from invoiceT full outer join transactionT on invoiceT.ReferenceNumber = transactionT.InvoiceNumber END If the fs_transaksie.trans_tipe field = 3 and fs_transaksie.rek = 5205 then an error message needs to be displayed to the user. He may NOT view the data
If you can just not display the data, rather than raising an error then changing your WHERE clause to: WHERE ( fs_transaksie.verwysnr = #ReceiptNumber ) AND (fs_transaksie.trans_tipe = 3) And (fs_transaksie.rek != 5205) should exclude it - you are already filtering it to just be trans_tipe = 3 so adding the And (fs_transaksie.rek != 5205) will exclude any where the results are 3 and 5205 respectively.
SQL Merge returning all the rows from target on match
I have the following SQL Merge statement. DECLARE #TmpTable TABLE (BusinessBaseID int, BatchID uniqueidentifier, SupplierID int, SupplierVenueID varchar(200), AddressID int,[Action] varchar(50)) DECLARE #noop int; -- needed for the NO-OP below Declare #TestOp Varchar(max) Set #TestOp = 't' -- Insert into BusinessBase retrieving all inserted BusinessBaseIDs mappings via tmptable -- Another SQL blck goes here to insert records into TEMP table MERGE Business.Address AS t USING (SELECT tmp.BusinessBaseID, tmp.BatchID, tmp.SupplierID, tmp.SupplierVenueID, v.Name, v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode, v.Latitude, v.Longitude, dbo.GetVenueId(v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode, 'GB', v.Latitude, v.Longitude) as VenueId FROM #TmpTable as tmp INNER JOIN Supplier.VenueImport as v ON tmp.BatchID = v.BatchID AND tmp.SupplierID = v.SupplierID AND tmp.SupplierVenueID = v.SupplierVenueID WHERE (tmp.BatchID = 'D7F369F1-A66A-4440-8D4B-2F521F672916') AND (tmp.SupplierID = 17) ) AS s ON S.VenueId >0 WHEN MATCHED THEN UPDATE SET #TestOp = #TestOp + ':' +convert(varchar, S.VenueId)+'|'+Convert(varchar,t.AddressId) -- the NO-OP instead of update WHEN NOT MATCHED BY TARGET THEN INSERT (AddressLine1, AddressLine2, City, StateProvince,PostalCode,CountryCode,Lat,Long) VALUES (AddressLine1, AddressLine2, City, County, PostalCode, 'GB', Latitude, Longitude) OUTPUT s.BusinessBaseID, s.BatchID, s.SupplierID, s.SupplierVenueID,ISNULL(INSERTED.AddressID,deleted.addressId),$action INTO #TmpTable; Select #TestOp; Select #Temp where [Action] = 'Update' Above query returning all the rows (except newly inserted records). Where as it suppose to return only 1 record as S.VenueId is greater than 0 for only one record. dbo.GetVenueId is a function which returns an integer. It will be > 0 for existing records and -1 for not existing records. Could somebody point me where I am doing wrong. Thanks, Naresh
Change the call dbo.GetVenueId to the following CASE dbo.GetVenueId(v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode, 'GB', v.Latitude, v.Longitude) WHEN -1 THEN -1 ELSE 0 END as VenueId So It will act well