Using SQL DataTables to speed up database performance

The issue with performing multiple SQL commands within a for loop.

If like me, you've found yourself with a web-based project that requires operations on hundreds of records simultaneously, you might have found yourself running a SQL insert/update/delete command for every one, but with the use of a C# DataTable, you'll be able to perform an action on all items with one command and it's a fairly simple change to get it all set up.

So an example of your slow running code might be something like this:

using (SqlCommand objCmd = new SqlCommand())
{
    objCmd.Parameters.Add("@itemGUID", SqlDbType.UniqueIdentifier).Value = objPlan.GUID;
    objCmd.Parameters.Add("@itemDate", SqlDbType.DateTime).Value = DateTime.Now;
    objCmd.Parameters.Add("@parentGUID", SqlDbType.UniqueIdentifier).Value = objPlan.Parent.GUID;
                    
   // Execute procedure and perform any other tasks
   // ...
}

That code will more than likely be wrapped in a for/foreach loop and the parameters would be updated using the object given in the loop. That's fine for a small number of operations, but that's a lot of SQL connections if you're doing this for hundreds of objects at the same time and you'll more than likely run into performance issues because of the round trip time for each connection, even if the SQL server is on the same box.

Building a DataTable to use as a SQL command object parameter.

The good news is that we can use a User Defined Table Type as a parameter on a single connection provide an entire table of records held in memory over to a stored procedure. It does introduce a little more complexity on the SQL Server instance, but not too much.

See the definition of a User Defined Table Type as described on the Microsoft TechNet website:

'In SQL Server 2008, a user-defined table type is a user-defined type that represents the definition of a table structure. You can use a user-defined table type to declare table-valued parameters for stored procedures or functions, or to declare table variables that you want to use in a batch or in the body of a stored procedure or function. For more information about how to define a table structure, see CREATE TABLE (Transact-SQL).'

Within your C# code, you'll need to change the above code to something like this...

using (SqlCommand objCmd = new SqlCommand())
{
    // Create a new DataTable - It's a lot quicker than doing each row individually.
    using (DataTable objDT = new DataTable())
    {
        // Define the Columns within the new DataTable
        objDT.Columns.Add("itemGUID", typeof(Guid));
        objDT.Columns.Add("itemParentGUID", typeof(Guid));
        objDT.Columns.Add("itemDate", typeof(DateTime));

        // Add each line to the new DataTable and make sure there are no duplicates.
        foreach (DeletedItem objDeletedItem in colNewToServerDeletedItems.GroupBy(x => x.ItemGUID).Select(y => y.First()))
        {
            objDT.Rows.Add(
                objDeletedItem.ItemGUID,
                objDeletedItem.ItemParentGUID, 
                objDeletedItem.Date
            );
        }

        // Add the DataTable to the command object.
        SqlParameter dtparam = objCmd.Parameters.AddWithValue("@myRecords", objDT);
        dtparam.SqlDbType = SqlDbType.Structured;

        // Execute procedure and perform any other tasks
        // ...
    }
}

Simple eh? We've defined a table within C# and added the rows to it by iterating the objects within our collection, then we execute the stored procedure as normal.

There are a couple of other changes we need to make this happen though. Remember I said above that it will add a little complexity to your SQL Server instance? Well, we're going to need to define the User Defined Table Type within the database and then add it as a parameter to the stored procedure that we expect to runt he operation.

Creating a SQL User Defined Table Type.

Whilst you can create a User Defined Table Type by coding the create script by hand, I usually find that because we're more than likely just going to perform a CRUD operation or more specifically just the C, U and D parts of a CRUD on the data, we can script the User Defined Table Type straight from the table it'll be used to manipulate.

Within SQL Server Management Studio, right-click the table we'll be performing operations on and expand 'Script Table as', then expand 'CREATE to' and click on 'New Query Editor Window', your scripting work has now been done for you, you just need to remove and constraints and change 'CREATE TABLE [dbo].[mytablename]' to 'CREATE TYPE [dbo].[mytabletypename] AS TABLE'.

Your script to create your User Defined Table Type should now look something like this.

CREATE TYPE [dbo].[mytabletypename] AS TABLE(
	[itemGUID] [uniqueidentifier] NOT NULL,
	[itemParentGUID] [uniqueidentifier] NOT NULL,
	[itemDate] [datetime] NOT NULL)
GO

Go ahead and execute that code and now it's just a matter of editing the stored procedure to use the new table type.

Structuring a stored procedure to use a User Defined table Type as a parameter.

Create a stored procedure similar to the one below, it must match your table and table type names and structures.

Note: The table type parameter must be read only.

CREATE PROCEDURE [dbo].[myprocedure]
	-- User defined table type as parameter
	@myRecords AS dbo.mytabletypename READONLY,
	-- Result
	@Return int OUTPUT
AS
SET NOCOUNT ON;
BEGIN TRANSACTION 

SET @Return = 1;

INSERT INTO mytablename (itemGUID, parentGUID, itemDate)
	SELECT itemGUID, parentGUID, itemDate
		FROM @myRecords

-- Execute
IF (@@error <> 0 OR @Return = -1) 
	BEGIN
		SET @Return = -1
		ROLLBACK TRANSACTION 
	END
ELSE
	BEGIN
	  COMMIT TRANSACTION
	END

Notice that the User Defined Table Type we defined earlier has been given as a parameter and we're able to perform select operations on it as if it were a real table. Using this select, we can insert every row provided into the actual table within the database.


Published at

Tags: SQL,MSSQL,C#

Luke Alderton

Comments

Share with
Tags
Latest Comments
By Mark Gentry on Windows Server 2019 - Change product key does nothing
20 Aug 2021, 03:30 AM
By Cathy on In-Place Upgrade for a Windows Server domain controller
31 Jul 2021, 18:28 PM
By Mr. Greymatter on Raspberry Pi - Running Java app on Raspbian
16 Feb 2021, 07:35 AM
By Mikko Seittenranta on Xamarin Forms multiple instances of same app open
16 Feb 2021, 04:34 AM
By Andrew on Auto/Custom height on Xamarin Forms WebView for Android and iOS
22 Jan 2021, 22:15 PM
By Nick on Raspberry Pi - Running Java app on Raspbian
14 Oct 2020, 19:37 PM
By Ivan on Fixed: Value cannot be null Parameter Name: source
15 Sep 2020, 19:47 PM
By Anand on Raspberry Pi - Bluetooth using Bluecove on Raspbian
7 Sep 2020, 16:53 PM
Categories
App Development
Event
Game Development
Mapping
Modelling
Programming
Review
Robotics
Tutorial
Web Development