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

Resources