SQL Server : use a transaction in the then clause of merge - sql

In a merge statement, when not matched, I need to insert into target table and update another table. How can I achieve this?
merge table1 as TARGET
using table2 as SOURCE ON <conditions>
when not matched by target
then
/*--------can i do this?---------*/
BEGIN TRANSACTION
insert (columnnames) values(v1,..., vn) /*insert into target*/
update source
set column1 = value1 /*update source*/
END TRANSACTION;

The whole merge statement is atomic (either the whole statement completes or the whole statement gets rolled back) so that you don't need to create a transaction.
Here's an example from Microsoft:
MERGE Production.UnitMeasure AS target
USING (SELECT #UnitMeasureCode, #Name) AS source (UnitMeasureCode, Name)
ON (target.UnitMeasureCode = source.UnitMeasureCode)
WHEN MATCHED THEN
UPDATE SET Name = source.Name
WHEN NOT MATCHED THEN
INSERT (UnitMeasureCode, Name)
VALUES (source.UnitMeasureCode, source.Name)
OUTPUT deleted.*, $action, inserted.* INTO #MyTempTable;

Related

SQL - If not exists

I have a table in DB where some records exist.
When I add a new record I need to check by column "Name" if the record exists in DB. If such a record does not exist - then add it, if exists - then update. I'm trying like this:
USE [TestDB]
GO
DECLARE #daily nvarchar = 'DailySummaryEmailProcessor'
IF NOT EXISTS ( SELECT *
FROM [dbo].Crons
WHERE name = #daily)
BEGIN
INSERT INTO [dbo].Crons (CronJobID, Name, Description)
VALUES()
END
ELSE
BEGIN
UPDATE [dbo].Crons
SET
WHERE
END
MERGE should fit your requirements MERGE (Transact-SQL):
MERGE Crons AS target
USING (SELECT #Name) AS source (Name)
ON (target.Name = source.Name)
WHEN MATCHED THEN
UPDATE SET target.Name = source.Name
WHEN NOT MATCHED
INSERT INTO Crons (Name) VALUES (source.Name)
OUTPUT $action, deleted.Name, inserted.Name
the issue is you never declare the size of the variable #dialy
try this and see what do you get
DECLARE #daily nvarchar = 'DailySummaryEmailProcessor'
SELECT #daily
Looks like you need to do INSERT .. UPDATE.. check out MERGE statement
https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-2017
EDIT:
Oh.. you have MySQL there in the tag. My answer is specifically on SQL Server.

How do you merge two different SQL Server 2012 database tables in single stored procedure?

MERGE [160.80.3.220].[sample].[dbo].[Products] AS TARGET
USING UpdatedProducts AS SOURCE ON (TARGET.ProductID = SOURCE.ProductID)
-- When records are matched, update
-- the records if there is any change
WHEN MATCHED AND TARGET.ProductName <> SOURCE.ProductName
OR TARGET.Rate <> SOURCE.Rate THEN
UPDATE
SET TARGET.ProductName = SOURCE.ProductName,
TARGET.Rate = SOURCE.Rate
-- When no records are matched, insert
-- the incoming records from source
-- table to target table
WHEN NOT MATCHED BY TARGET THEN
INSERT (ProductID, ProductName, Rate)
VALUES (SOURCE.ProductID, SOURCE.ProductName, SOURCE.Rate)
-- When there is a row that exists in target table and
-- same record does not exist in source table
-- then delete this record from target table
WHEN NOT MATCHED BY SOURCE THEN
DELETE
-- $action specifies a column of type nvarchar(10)
-- in the OUTPUT clause that returns one of three
-- values for each row: 'INSERT', 'UPDATE', or 'DELETE',
-- according to the action that was performed on that row
OUTPUT $action,
DELETED.ProductID AS TargetProductID,
DELETED.ProductName AS TargetProductName,
DELETED.Rate AS TargetRate,
INSERTED.ProductID AS SourceProductID,
INSERTED.ProductName AS SourceProductName,
INSERTED.Rate AS SourceRate;
SELECT ##ROWCOUNT;
GO
Since:
target_table cannot be a remote table. target_table cannot have any
rules defined on it.
What you could do is first insert all the data from your linked server to your current server database table using four-part query, then do Merge.
OR:
using source table as remote table because remote table is supported in USING. So what you could do alternatively is:
first Change connection to [160.80.3.220].[sample]
then:
MERGE [dbo].[Products] AS TARGET
USING [linked server instance].[database].[schema].UpdatedProducts AS SOURCE

MERGE statement DELETE alternative in SQL Server

I have a Query using T-SQL MERGE Statement. Due to performance issues I am re writing the Query using IF Exists Update and If Not Exists Insert. I am able to write Insert/Update without any issue. But I am unable to handle the DELETE. Can some one please help me on this?
Here is the sample
---SAMPLE MERGE STATEMENT
MERGE
member_topic AS target
USING
someOtherTable AS source
ON
target.mt_member = source.mt_member
WHEN MATCHED THEN
UPDATE SET target.mt_notes = source.mt_notes
WHEN NOT MATCHED THEN
INSERT (mt_member, mt_topic, mt_notes) VALUES (source.mt_member, source.mt_notes)
WHEN NOT MATCHED BY SOURCE THEN
DELETE member_topic;
--UPDATE
UPDATE T SET T.mt_notes = S.mt_notes
FROM member_topic T
JOIN someOtherTable S ON T.mt_member=S.mt_member
--INSERT
INSERT INTO member_topic(mt_member, mt_topic, mt_notes)
SELECT mt_member, mt_topic, mt_notes
FROM someOtherTable S
WHERE NOT EXISTS(SELECT 1
FROM member_topic T
WHERE T.mt_member=S.mt_member)
How to handle
WHEN NOT MATCHED BY SOURCE THEN
DELETE member_topic;
in single DELETE Statement.
a sample script to be embedded between begin and end in proc
MERGE dbo.Tablet AS TARGET
USING dbo.QueryView AS SOURCE
ON (
TARGET.[ID] = SOURCE.[ID]
)
WHEN MATCHED
THEN
UPDATE SET
TARGET.[ID] = SOURCE.[ID]
WHEN NOT MATCHED BY TARGET THEN
INSERT (ID, [Name] )
VALUES (SOURCE.[ID], SOURCE.[Name] )
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
Try
DELETE T
FROM member_topic T
WHERE NOT EXISTS(SELECT 1
FROM someOtherTable S
WHERE T.mt_member=S.mt_member)
DELETE t
FROM member_topic t
LEFT JOIN someOtherTable s ON t.mt_member = s.mt_member
WHERE s.mt_member IS NULL

Write a MERGE Statement for mis-matching values

I have a table, tblstars_new, which is updated weekly from the client. I have another table, tblstars, which needs to import new and updated data from tblstars_new.
Finding rows in tblstars_new that do not exist in tblstars, and then adding then to tblstars is simple.
But, I also need to find rows in tblstars_new in which the column PandA_Code has changed, and then update the identical row in tblstars.
This query tells me which rows from tblstars_new have PandA_Code's that have changed and need to be updated in tblstars.
SELECT
sn.*
FROM
tblstars_new sn
JOIN tblstars s ON sn.Student_ID_Number = s.Student_ID_Number AND sn.PandA_Code != s.PandA_Code
I'm trying to figure out a MERGE statement that will make the changes. As I'm doing in Prod, I can't really play around. Two questions:
1) Is it possible to see the changes without actually doing them?
2) Is the MERGE statement below correct?
BEGIN TRAN;
MERGE tblstars AS T -- Target
USING tblstars_new AS S -- Source
ON
(T.Student_ID_Number = S.Student_ID_Number AND T.PandA_Code != S.PandA_Code)
WHEN NOT MATCHED BY TARGET
THEN
UPDATE SET T.PandA_Code = S.PandA_Code
OUTPUT $action;
ROLLBACK TRAN;
GO
Here is the MERGE query that satisfy your requests.
BEGIN TRAN;
MERGE tblstars AS T -- Target
USING tblstars_new AS S -- Source
ON T.Student_ID_Number = S.Student_ID_Number -- They shoudl match by PK
WHEN MATCHED AND AND T.PandA_Code != S.PandA_Code
THEN -- when matched and PandA_Code different update them
UPDATE SET T.PandA_Code = S.PandA_Code
WHEN NOT MATCHED BY TARGET -- When not matched by TARGET (there is in source but not in target)
THEN INSERT (<field1, field2, ...>)
VALUES (<S.field1, S.filed2, ...>) -- then insert them
OUTPUT deleted.*, $action, inserted.* INTO #TheTempTable;
SELECT * FROM #TheTempTable; -- here you can see the cnages and rollback if something is wrong
ROLLBACK TRAN;
GO
This is simply done by using join instead of merge statement:
UPDATE T SET T.PandA_Code = S.PandA_Code FROM tblstart T
INNER JOIN tblstars_new P ON P.Student_ID_Number = T.Student_ID_Number
WHERE T.PandA_Code <> S.PandA_Code

Showing column twice in a query, second time showing data post update/set

For testing purposes I'm wondering if the following two selects could ever be combined:
begin transaction
select x
from example_table
update example_table
set x = 'new value'
select x
from example_table
rollback transaction
Essentially, I'd like to see a result output like:
Column Name, Updated Column Name
If I understand you correctly you can do in using OUTPUT clause (SQL Server 2005 +)
use tempdb
go
create table #tbl (i int)
insert into #tbl values (10),(20),(30),(40)
update #tbl
set i=i+1
output deleted.i i_old,inserted.i i_updated
drop table #tbl
From Books OnLine
OUTPUT Clause returns information from, or expressions based on, each
row affected by an INSERT, UPDATE, DELETE, or MERGE statement.
...
DELETED is a column prefix that specifies the value deleted by the
update or delete operation. Columns prefixed with DELETED reflect the
value before the UPDATE, DELETE, or MERGE statement is completed.
...
INSERTED is a column prefix that specifies the value added by the
insert or update operation. Columns prefixed with INSERTED reflect the
value after the UPDATE, INSERT, or MERGE statement is completed but
before triggers are executed.
EDITED
begin transaction
update example_table
set x = 'new value'
output deleted.x old_value, inserted.x new_value
rollback transaction