While working with table records from more than one table, there is often need of synchronizing data across many tables. For this purpose, we generally write UPDATE, INSERT and DELETE commands one after another based upon certain conditions. Oracle has already MERGE command to perform inserts and updates into a single table in a single command since Oracle9i. But with SQL Server 2008, there is MERGE command to do updates, inserts and deletes in a single statement. MERGE statement will take the source data from a table or view or query, and then perform DML operations (insert, update, delete) if the right conditions match.
Let’s see from an example where we have two tables: dbo.BusinessClients_Old as ‘Target’ table and dbo.BusinessClients as ‘Source’ table. We are going to synchronize the records between these two tables. We will update ‘ContactNo’ in Target table, insert new record into Target table that is only in Source table and delete unmatched record in Target table. The result will be synchronized records between both the tables.
Fig 1: Initial data found in both the tables
Now, issue a MERGE command as:
MERGE dbo.BusinessClients_Old as tblTarget
USING dbo.BusinessClients as tblSource
ON (tblTarget.ID = tblSource.ID)
WHEN MATCHED THEN
UPDATE SET tblTarget.ContactNo = tblSource.ContactNo
WHEN NOT MATCHED BY SOURCE THEN
WHEN NOT MATCHED THEN
INSERT (Name, Email, Profession, ContactNo, ID)
VALUES (tblSource.Name, tblSource.Email, tblSource.Profession, tblSource.ContactNo, tblSource.ID)
OUTPUT $action, inserted.id as InsertedId, inserted.name AS InsertedName, inserted.contactno AS InsertedContactNo,
deleted.id AS DeletedId, deleted.name AS DeletedName;
The result is shown in figure below. Fig 2: Result of MERGE command that is synchronized records now
See the updated ContactNo and inserted new record in the Target table: dbo.BusinessClients_Old. Fig 3: Synchronize records in Target table: dbo.BusinessClients_Old
Points to be noted:
MERGE statement must end with a semicolon. The OUTPUT clause is optional here which is used to show recently inserted or updated or deleted records in the same session by INSERT or UPDATE or DELETE statement. This OUTPUT clause is there since SQL Server 2005. We may be aware of ‘inserted’ and ‘deleted’ tables if we have made our hands dirty by writing triggers. ‘inserted’ and ‘deleted’ are temporary tables created automatically by SQL Server whenever there is row modification in a table.
We can remove the OUTPUT clause, but it is there to see what all modifications happen when issuing MERGE command. ‘$action’ is a nvarchar(10) column that shows which action was performed by the MERGE command: INSERT or UPDATE or DELETE based upon the condition specified. Better to write ‘$action’ always in lower case as its case is dependent upon database collation for case sensitiveness. For WHEN clause, we have to be little careful. I suggest to refer to SQL Server Books Online at http://msdn.microsoft.com/en-us/library/ms130214.aspx for more details on WHEN clause.
With the growing complexity of database applications, we often come across a problem when we need to send multiple rows to database table(s). When SqlBulkCopy class was introduced in .Net 2.0, it was a great relief for all. But if the same requirement arises at back-end processing and mostly in Stored Procedures, programmers often feel something lacking in stored procedure parameters. Workaround solutions are temporary tables, cursors, etc. to overcome.
SQL Server 2008 has a new feature called Table-Valued parameter where we can pass a table valued parameter to stored procedure just like we pass Entity or DTO class object as parameter to Data Access Layer in C# or VB.Net.
Now we can directly pass table like object instead of processing row by row ranging from few to many rows. We used SqlBulkCopy class to post many rows, but still it was not able to pass a table as a parameter.
In this example, we will see how to create a table-value type object, pass it to a stored procedure as a parameter in Sql Server 2008. Also, how to use it programmatically in C# 3.5 as well.
–Step 1: Create a User-defined Table Type
CREATE TYPE PublisherTableType AS TABLE
–Step 2: Create a destination table
CREATE TABLE [dbo].[BookPublisher](
[PublisherId] [bigint] NULL,
[PublisherName] [varchar](50) NULL,
[PublisherAddress] [varchar](50) NULL,
[PublisherContactNo] [varchar](15) NULL
) ON [PRIMARY]
–Step 3: Create a procedure to receive data for the table-valued parameter
CREATE PROCEDURE usp_InsertPublisherInfo
@PublisherInfo dbo.PublisherTableType READONLY
SET NOCOUNT ON
INSERT INTO dbo.BookPublisher(PublisherID,PublisherName,PublisherAddress,PublisherContactNo)
SELECT * FROM @PublisherInfo;
–Step 4: Declare a variable of type PublisherTableType
Declare @PubInfo dbo.PublisherTableType;
Insert into @PubInfo
values (1, ‘BPB’, ‘Delhi, India’, ‘1234567890’ ),
(2, ‘PHI’, ‘Mumbai, India’, ‘7892345678’),
(3, ‘Apress’, ‘Chennai, India’, ‘467890034’),
(4, ‘Wrox’, ‘Kolkata, India’, ‘7463643847’);
–Step 5: Execute SP by passing table value type
Exec usp_InsertPublisherInfo @PublisherInfo = @PubInfo;
–Step 6: Select the newly inserted records
Select * from dbo.BookPublisher;
I think steps mentioned in the above example are quite clear. One can see this newly created table value type object under “User-Defined Table Types”. Now, let’s look into another example to exploit the newly created PublisherTableType.
private DataTable PublisherList()
DataTable TblPub = new DataTable();
//Fill the collection values
for (Int16 RwCnt = 0; RwCnt < 10; RwCnt++)
PubRow = TblPub.NewRow();
PubRow = 100 + RwCnt;
PubRow = “BookPub” + ” – ” + RwCnt.ToString();
PubRow = “PubAddress” + ” – ” + RwCnt.ToString();
PubRow = “0091” + ” – ” + “4567” + RwCnt.ToString();
private void SaveDataUsingTableValueType(DataTable PubList)
String ConString = @”Data Source=DINESH_PC\DINESHPC;Initial Catalog=AdventureWorks;Integrated Security=True”;
SqlConnection SqlCon = new SqlConnection(ConString);
String SpName = “usp_InsertPublisherInfo”;
SqlCommand SqlCmd = new SqlCommand(SpName, SqlCon);
SqlCmd.CommandType = System.Data.CommandType.StoredProcedure;
//Parameter type is SqlDbType.Structured for table valued type parameter
SqlParameter SqlParam = SqlCmd.Parameters.Add(“@PublisherInfo”, System.Data.SqlDbType.Structured);
SqlParam.Value = PubList;
//Execute the command
Next, call the save method as: SaveDataUsingTableValueType(PublisherList());
This way we see how this new feature is a bliss.
We have to remember some points with table value type. The READONLY qualifier after table value parameter in stored procedure is required here to mark parameter object as a pointer to the original table value. This way the original content passed cannot be modified inside the stored procedure. Once a table type object is created, it cannot be altered other than dropping and recreating it again with modified definition. Performance wise it is well suited for table rows less than 1000.
Have a nice coding day!CodeProject
I think we all are familiar with
INSERT statement in Sql Server 7.0/2000/2005, and may be in Oracle, MySQL, etc. In Sql Server, we have come across
INSERT INTO TableName (columns) VALUES (literalValues), or
INSERT INTO TableName (columns) SELECT columns FROM TableName
X statement many times. The first type of statement is used when we have to insert one record at a time, while the second statement helps us to insert one or more records depending upon the number of rows returned by
SELECT statement. Some even use
UNION clause to select multiple rows.
We also know how painful it was to insert multiple records using
INSERT INTO TableName (columns) VALUES (literalValues) from DataAccessLayer although different programmers have different approaches to it.
But Sql Server 2008 has now come with a rescue for all programmers with a new feature called “Row Constructor“. Yes, a Class like initialization constructor!
Let’s see it by example.
–Create a new table [BusinessClients] with row values from the list [WHERE Age <= 30]
SELECT Name, Email, Profession, ContactNo
VALUES(‘Dinesh’, ‘firstname.lastname@example.org’,’Software Engineer’, ‘9899144254’, 28),
(‘Ravindra’, ‘email@example.com’, ‘Telecom Engineer’, ‘9887345678’, 28),
(‘Rosan’, ‘firstname.lastname@example.org’, ‘Social Activist’, ‘9891234560’, 30),
(‘Hari’, ‘email@example.com’, ‘Construction Engineer’, ‘9897654890’, 32)
) as Employee(Name, Email, Profession, ContactNo, Age)
WHERE Age <= 30;
–Fetch the inserted records from a newly created table
Select * from dbo.BusinessClients;
–Select records from the row constructors
SELECT Name, Email, Profession, ContactNo
VALUES(‘DineshMandal’, ‘firstname.lastname@example.org’,’Software Engineer’, ‘9899144254’, 28),
(‘RavindraGhimire’, ‘email@example.com’, ‘Telecom Engineer’, ‘9887345678’, 28),
(‘RosanSapkota’, ‘firstname.lastname@example.org’, ‘Social Activist’, ‘9891234560’, 30),
(‘HariAdhikari’, ‘email@example.com’, ‘Construction Engineer’, ‘9897654890’, 32)
) as EmployeeDetails(Name, Email, Profession, ContactNo, Age)
WHERE Age <= 30;
See the VALUES clause in parenthesis and separated by comma. This is what we call “Row Constructor”. In example 2, see the derived table name EmployeeDetails and its column names inside the parenthesis. And of course the list of column names we like to select out of VALUES parameter values. These are few things we should take care when using row constructor.
I think this feature is really praiseworthy.