How to update the source table in the MERGE statement? - sql

I have a table tbBroker on my local database and a table Agency on linked server object "DIS".
I am trying to migrate data from local's tbBroker to DIS's Agency table
Also note that
tbBroker's Columns = Agency's Columns
BrokerCode = AgencyNumber
BusinesssName = AgencyName
City = City
tbSystemUser.EmailAddress = EmailAddress
Also, tbBroker has two additional columns DISImportFlag bit and DISCreatTS datetime. These two fields should be updated to 1 and GETDATE() whenever data is migrated (during insertion) from tbBroker to Agency table in order to determine which rows have been migrated.
I have written the following query
USE [DISTemp];
MERGE INTO
[dbo].[Agency] AS [TARGET]
USING
[aginbr].[dbo].[tbBroker] AS [SOURCE]
ON
[TARGET].[AgencyNumber] COLLATE Latin1_General_CI_AI = [SOURCE].[BrokerCode]
WHEN NOT MATCHED BY TARGET THEN
INSERT (
[AgencyName]
, [Address1]
, [Address2]
, [PostalCode]
, [City]
, [Phone]
, [EmailAddress]
)
VALUES (
[SOURCE].[BUSINESSNAME]
, [SOURCE].[ADDRESS]
, [SOURCE].[AddressLine2]
, [SOURCE].[Zip]
, [SOURCE].[City]
, [SOURCE].[Phone]
, [SOURCE].[Email]
)
UPDATE SET [SOURCE].[DISImportFlag] = 1,[SOURCE].[DISCreatTS] = GETDATE()
WHEN MATCHED THEN
UPDATE SET
[TARGET].[AgencyName] = [SOURCE].[BUSINESSNAME]
, [TARGET].[Address1] = [SOURCE].[ADDRESS]
, [TARGET].[Address2] = [SOURCE].[AddressLine2]
, [TARGET].[PostalCode] = [SOURCE].[Zip]
, [TARGET].[City] = [SOURCE].[City]
, [TARGET].[Phone] = [SOURCE].[Phone]
, [TARGET].[EmailAddress] = [SOURCE].[Email];
When we execute the above query it throws error message
A MERGE statement must be terminated by a semi-colon (;)
after adding the following line
UPDATE SET [SOURCE].[DISImportFlag] = 1,[SOURCE].[DISCreatTS] = GETDATE()
Edit from comments
The source table tbBroker has a primary key column BrokerID.
I want to update in the source table those rows that were inserted into the target table, i.e. those rows that didn't exist in the target table before MERGE.

I don't think you can change some table other than TARGET using a single MERGE statement. But, you can use OUTPUT clause to capture the result of the MERGE in a helper/temporary table and then update your SOURCE based on that.
You said that you want to update only those rows that were inserted into the Target, i.e. those that didn't exist in the Target before MERGE.
Output IDs of inserted rows into the temporary table and then use it to update the Source table.
CREATE TABLE #Temp(
BrokerID int NOT NULL,
CONSTRAINT [PK_Broker] PRIMARY KEY CLUSTERED
(
BrokerID ASC
));
INSERT INTO #Temp (BrokerID)
SELECT TableChanges.BrokerID
FROM
(
MERGE INTO [dbo].[Agency] AS [TARGET]
USING [aginbr].[dbo].[tbBroker] AS [SOURCE]
ON [TARGET].[AgencyNumber] COLLATE Latin1_General_CI_AI = [SOURCE].[BrokerCode]
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
[AgencyName]
, [Address1]
, [Address2]
, [PostalCode]
, [City]
, [Phone]
, [EmailAddress]
)
VALUES
(
[SOURCE].[BUSINESSNAME]
, [SOURCE].[ADDRESS]
, [SOURCE].[AddressLine2]
, [SOURCE].[Zip]
, [SOURCE].[City]
, [SOURCE].[Phone]
, [SOURCE].[Email]
)
WHEN MATCHED THEN
UPDATE SET
[TARGET].[AgencyName] = [SOURCE].[BUSINESSNAME]
, [TARGET].[Address1] = [SOURCE].[ADDRESS]
, [TARGET].[Address2] = [SOURCE].[AddressLine2]
, [TARGET].[PostalCode] = [SOURCE].[Zip]
, [TARGET].[City] = [SOURCE].[City]
, [TARGET].[Phone] = [SOURCE].[Phone]
, [TARGET].[EmailAddress] = [SOURCE].[Email]
OUTPUT $action, [SOURCE].BrokerID
) AS TableChanges (MergeAction, BrokerID)
WHERE TableChanges.MergeAction = 'INSERT'
;
UPDATE [aginbr].[dbo].[tbBroker]
SET
[DISImportFlag] = 1
,[DISCreatTS] = GETDATE()
WHERE
BrokerID IN
(
SELECT T.BrokerID FROM #Temp AS T
)
;
DROP TABLE #Temp;
Obviously, wrap this whole thing in a transaction and TRY ... CATCH and add appropriate error handling.

Related

i want copy data from table to another without duplicate using triggers

i have two tables
in table number one it's add rows every moment depend on user insert
and in table number two i want to copy every row has been added on table one but with specific criteria
and also table number two have some additional columns i need them later
i want do that by triggers but i don't know how to do that
i'v already try stored procedure but not automatically
INSERT INTO ax.RETAILTRANSACTIONSALESTRANS2
(
[BARCODE]
, [CREATEDDATETIME]
,[Name]
, [ITEMID]
, [NETAMOUNTINCLTAX]
, [QTY]
, [STAFFID]
, [TERMINALID]
, [TRANSDATE]
,[PERIODICPERCENTAGEDISCOUNT]
,[ReceiptID]
)
SELECT ab.[BARCODE]
, ab.[CREATEDDATETIME]
,[ax].[ECORESPRODUCTTRANSLATION].[NAME]
, ab.[ITEMID]
, ab.[PRICE]
, ab.[QTY]
, ab.[STAFFID]
, ab.[TERMINALID]
, ab.[TRANSDATE]
, ab.[PERIODICPERCENTAGEDISCOUNT]
,ab.RECEIPTID
FROM ax.RetailTransactionSalesTrans ab
inner join [ax].[INVENTTABLE] on ab.[ITEMID] =[ax].[INVENTTABLE].[ITEMID]
inner join [ax].[ECORESPRODUCTTRANSLATION] on [ax].[INVENTTABLE].PRODUCT =[ax].[ECORESPRODUCTTRANSLATION].[PRODUCT]
LEFT JOIN ax.RETAILTRANSACTIONSALESTRANS2 a ON
a.[BARCODE] = ab.[BARCODE]
AND a.[CREATEDDATETIME] = ab.[CREATEDDATETIME]
AND a.[ITEMID] = ab.[ITEMID]
AND a.[NETAMOUNTINCLTAX] = ab.[PRICE]
AND a.[QTY] = ab.[QTY]
AND a.[STAFFID] = ab.[STAFFID]
AND a.[TERMINALID] = ab.[TERMINALID]
AND a.[TRANSDATE] = ab.[TRANSDATE]
AND a.[PERIODICPERCENTAGEDISCOUNT]=ab.[PERIODICPERCENTAGEDISCOUNT]
and a.Process=null and a.Checked=null and a.[Select]=null
where ab.[QTY]>0 and ab.[RECEIPTID]!='' and ab.[TRANSDATE] >= DATEADD(day, #daycount*-1, GetDate())
and NOT EXISTS(select * from ax.RETAILTRANSACTIONSALESTRANS2 where [CREATEDDATETIME] = ab.[CREATEDDATETIME])
i want to convert that as trigger but i don't how do that
note: #daycount i take it from another table but i pass that by my program
you can use the below and edit the needed columns , the idea is FROM Inserted you can select all rows that inserted to the main table.
CREATE TRIGGER TRetailTransactionSalesTrans ON RetailTransactionSalesTrans
FOR INSERT
AS
BEGIN
INSERT INTO RETAILTRANSACTIONSALESTRANS2
SELECT [BARCODE]
,[CREATEDDATETIME]
,[Name]
,[ITEMID]
,[NETAMOUNTINCLTAX]
,[QTY]
,[STAFFID]
,[TERMINALID]
,[TRANSDATE]
,[PERIODICPERCENTAGEDISCOUNT]
,[ReceiptID]
FROM Inserted;
END

Stored procedure to Insert data between tables

I want to insert data from a table called temp_menu into another called menu.
They have the same structure, they store the same data, I want to create a stored procedure to check the differences between the tables. If there are any different rows and the rows don't exist in table menu, I want to insert them into menu; if the rows exists, I want to update the rows in menu if the DateReg column is higher that the DateReg column in the temp_menu table.
The tables have this structure:
CREATE TABLE [dbo].[Menu_Temp]
(
[Date] [datetime] NOT NULL,
[Ref] [int] NOT NULL,
[Art] [char](60) NOT NULL,
[Dish] [char](60) NOT NULL,
[DateReg] [datetime] NOT NULL,
[Zone] [char](60) NOT NULL,
);
I have this code to check for differences between the tables:
SELECT *
INTO #diffs
FROM [Regi].dbo.menu
EXCEPT
SELECT * FROM [Regi].dbo.menu_Temp
IF ##ROWCOUNT = 0
RETURN
SELECT * FROM #diffs
Full details are here : https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql
An example for your situation could be...
MERGE
[Regi].dbo.menu
USING
[Regi].dbo.menu_Temp
ON (menu_Temp.[Ref] = menu.[Ref]) -- Assumes [Ref] is the identifying column?
WHEN
MATCHED AND (menu_Temp.[DateReg] > menu.[DateReg])
THEN
UPDATE SET [Art] = menu_Temp.[Art],
[Dish] = menu_Temp.[Dish],
[Zone] = menu_Temp.[Zone],
[Date] = menu_Temp.[Date],
[DateReg] = menu_Temp.[DateReg]
WHEN
NOT MATCHED
THEN
INSERT (
[Date],
[Ref],
[Art],
[Dish],
[DateReg],
[Zone]
)
VALUES (
menu_Temp.[Date],
menu_Temp.[Ref],
menu_Temp.[Art],
menu_Temp.[Dish],
menu_Temp.[DateReg],
menu_Temp.[Zone]
)
http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b47d7e879856ffe6210589f6bb64829f

SQL Server 2008 - Stored inserted IDs in a variable table for another query use later

I have an INSERT query to insert multiple records and returns a set if CompanyID that inserted, and this query is working fine. I would like to stored those returned CompanyIDs into a table variable, so I can run another query after that using those CompanyIDs. I tried, but got stuck.
Please help.
-- Create a table variable
DECLARE #CompanyIDs TABLE
(
CompanyID INT NOT NULL
)
INSERT INTO #CompanyIDs -- STUCK right here and it failed
-- This INSERT query is working fine, and will return the CompanyIDs that inserted.
INSERT INTO [Apps].[dbo].[ERP_Company]
(
[Name]
,[Type]
,[ImportFrom]
,[InActiveFlag]
,[CreatedDate]
,[CreatedBy]
,[CompanyOwnerID]
,[ModifiedDate]
,[HQAddress]
,[HQCity]
,[HQState]
,[HQZip]
,[Type2]
,[Segment]
,[Industry]
)
output inserted.CompanyID
SELECT DISTINCT(Company) AS Company
, 'End-User'
, 'MarketingUpload'
, '0'
, GETDATE()
, '1581'
, '1581'
, GETDATE()
, [Address]
, City
, [State]
, ZipCode
, 'Customer'
, Segment
, Industry
FROM dbo.Import_CompanyContact icc
WHERE RefNum = 14
AND MatchFlag = 3
AND NOT Exists (SELECT * FROM dbo.ERP_Company
WHERE REPLACE(Name, '''', '') = REPLACE(icc.Company, '''', '')
)
OUTPUT inserted.CompanyID INTO #YourTable

Using SQL MERGE with vars instead of a table

take the following table (a very simplified example):
CREATE TABLE [dbo].[tbl_Order_Lines] (
[LineID] [int] IDENTITY(1, 1) NOT NULL ,
[OrderID] [int] NOT NULL ,
[StockCode] [varchar](20) NOT NULL ,
[Quantity] [smallint] NOT NULL
)
I have a proc that controls insertions into this table, but at one point you come to the infamous "UPSERT" scenario.
Let’s assume my procedure has the following vars:
#OrderID INT ,
#StockCode VARCHAR(20) ,
#Quantity SMALLINT
I currently do as follows:
IF ( NOT EXISTS ( SELECT *
FROM [dbo].[tbl_Order_Lines]
WHERE [OrderID] = #OrderID
AND [StockCode] = #StockCode )
)
INSERT INTO [dbo].[tbl_Order_Lines]
( [OrderID] ,
[StockCode] ,
[Quantity]
)
VALUES ( #OrderID ,
#StockCode ,
#Quantity
)
ELSE
UPDATE [dbo].[tbl_Order_Lines]
SET Quantity = #Quantity
WHERE [OrderID] = #OrderID
AND [StockCode] = #StockCode
My intention is to do away with this old method and use the MERGE statement - however i'm struggling to get my head round the MERGE statement, this is what i have so far:
MERGE dbo.tbl_Order_Lines
USING (
VALUES
( #Quantity
) ) AS Source ( Quantity )
ON dbo.tbl_Order_Lines.OrderID = #OrderID AND StockCode = #StockCode
WHEN MATCHED THEN
UPDATE SET Quantity = source.Quantity
WHEN NOT MATCHED THEN
INSERT (
OrderID ,
StockCode ,
Quantity
) VALUES
( #OrderID ,
#StockCode ,
Source.Quantity
);
My Question(s):
My attempt at this MERGE seems to work - yet it looks VERY messy and confusing - is there a better way of writing this?
How would i modify this MERGE statement to DELETE matching rows (based on OrderID & StockCode) if #Quantity = 0
You could tighten up the last part by using the special $action keyword.
Case $action
When 'INSERT' Then 'OK_ADDED'
When 'UPDATE' Then 'OK_UPDATED'
When 'DELETE' Then 'OK_REMOVED'
End
$action
Is available only for the MERGE statement. Specifies a column of type nvarchar(10) in the OUTPUT clause in a MERGE statement that returns one of three values for each row: 'INSERT', 'UPDATE', or 'DELETE', according to the action that was performed on that row.
Output Clause.
OK, this is what i came up with:
MERGE dbo.tbl_Order_Lines
USING ( VALUES ( #Quantity ) ) AS Source ( Quantity )
ON dbo.tbl_Order_Lines.OrderID = #OrderID AND StockCode = #StockCode
WHEN MATCHED AND #Quantity > 0 THEN
UPDATE SET Quantity = source.Quantity
WHEN MATCHED AND #Quantity <= 0 THEN
DELETE
WHEN NOT MATCHED AND #Quantity > 0 THEN
INSERT (
OrderID ,
StockCode ,
Quantity
)
VALUES
( #OrderID ,
#StockCode ,
Source.Quantity
)
OUTPUT
COALESCE(Inserted.LineID, Deleted.LineID) AS ResultID ,
CASE WHEN Deleted.LineID IS NULL
AND Inserted.LineID IS NOT NULL THEN 'OK_ADDED'
WHEN Deleted.LineID IS NOT NULL
AND Inserted.LineID IS NOT NULL THEN 'OK_UPDATED'
WHEN Deleted.LineID IS NOT NULL
AND Inserted.LineID IS NULL THEN 'OK_REMOVED'
END AS ResultDesc
INTO #tbl_LineChanges ( ResultID, ResultDesc );
Would still love to know if there is a tider way of writing this!

how to insert multiple rows with check for duplicate rows in a short way

I am trying to insert multiple records (~250) in a table (say MyTable) and would like to insert a new row only if it does not exist already.
I am using SQL Server 2008 R2 and got help from other threads like SQL conditional insert if row doesn't already exist.
While I am able to achieve that with following stripped script, I would like to know if there is a better (short) way to do this as I
have to repeat this checking for every row inserted. Since we need to execute this script only once during DB deployment, I am not too much
worried about performance.
INSERT INTO MyTable([Description], [CreatedDate], [CreatedBy], [ModifiedDate], [ModifiedBy], [IsActive], [IsDeleted])
SELECT N'ababab', GETDATE(), 1, NULL, NULL, 1, 0
WHERE NOT EXISTS(SELECT * FROM MyTable WITH (ROWLOCK, HOLDLOCK, UPDLOCK)
WHERE
([InstanceId] IS NULL OR [InstanceId] = 1)
AND [ChannelPartnerId] IS NULL
AND [CreatedBy] = 1)
UNION ALL
SELECT N'xyz', 1, GETDATE(), 1, NULL, NULL, 1, 0
WHERE NOT EXISTS(SELECT * FROM [dbo].[TemplateQualifierCategoryMyTest] WITH (ROWLOCK, HOLDLOCK, UPDLOCK)
WHERE
([InstanceId] IS NULL OR [InstanceId] = 1)
AND [ChannelPartnerId] IS NULL
AND [CreatedBy] = 1)
-- More SELECT statements goes here
You could create a temporary table with your descriptions, then insert them all into the MyTable with a select that will check for rows in the temporary table that is not yet present in your destination, (this trick in implemented by the LEFT OUTER JOIN in conjunction with the IS NULL for the MyTable.Description part in the WHERE-Clause):
DECLARE #Descriptions TABLE ([Description] VARCHAR(200) NOT NULL )
INSERT INTO #Descriptions ( Description )VALUES ( 'ababab' )
INSERT INTO #Descriptions ( Description )VALUES ( 'xyz' )
INSERT INTO dbo.MyTable
( Description ,
CreatedDate ,
CreatedBy ,
ModifiedDate ,
ModifiedBy ,
IsActive ,
IsDeleted
)
SELECT d.Description, GETDATE(), 1, NULL, NULL, 1, 0
FROM #Descriptions d
LEFT OUTER JOIN dbo.MyTable mt ON d.Description = mt.Description
WHERE mt.Description IS NULL