In one of my new assignments, I had to import a large volume of data into a SQLServer database.

Each imported data must either be updated if it already exists or created if it does not exist.

The first solution I came up with was to make three imported data requests:

  • the first by sending the dIdId list that they want to import (the server responding to the existing ones)

  • the second for inserts

  • the third for updates

This method has the disadvantage of dividing imports into 3 different SQL requests Which means we have the possibility that the SQL server fails in 3 places instead of 1 and we also have 3 back and forth with that server...

The ideal is to send the full list of imports to the base in a single SQL query data and it inserts or updates them directly.

Since SQLServer can handle complex types, one can therefore create a stored procedure that allows processing to be done in one go.

Implementation:

This requires three steps.

You need to create a TYPE SQLServer:

CREATE TYPE TalbetType AS TABLE 
( 
  Id uniqueidentifier NOT NULL,
  Name[nvarchar](MAX)NOT NULL,  
  ExternalId varchar(MAX) NOT NULL, 
  Type int NULL
);

The storage procedure must be established:

CREATE PROCEDURE [dbo].[SaveTablets]
(
    @Tablets [dbo].[TalbetType] READONLY
)
AS
BEGIN
    MERGE [dbo].[Tablets ] as target
    USING @Tablets AS source
    ON target.[ExternalId ] = source.[ExternalId]
    WHEN MATCHED THEN
    UPDATE SET target.[Id] = source.[Id],
    target.[ExternalId] = source.[ExternalId],
    target.[Name] = source.[Name],
    target.[Type] = source.[Type]
    WHEN NOT MATCHED THEN
    INSERT ([Id],[Name],[ExternalId],[Type])
    VALUES (source.[Id],source.[Name], source.[ExternalId], source.[Type]);
END


And then to call the stored procedure , you have to do the following:

            //Creation du datatable correspondant au type TabletType
            var dataTable = new DataTable("dt");
            dataTable.Columns.Add("Id", typeof(Guid));
            dataTable.Columns.Add("Name", typeof(string));
            dataTable.Columns.Add("ExternalId", typeof(string));
            dataTable.Columns.Add(new DataColumn("Type", typeof(int)) { AllowDBNull = true });        
            foreach (var tablet in tablets)
            {
                DataRow dataRow = dataTable.NewRow();
                dataRow["Id"] = tablet.Id;
                dataRow["Name"] = tablet.Name;
                dataRow["Email "] = tablet.Email;
                dataRow["ExternalId"] = tablet .ExternalId;
                if (tablet.Type.HasValue)
                    dataRow["Type"] = attendee.Type.Value;
                else
                    dataRow["Type"] = DBNull.Value;
                dataTable.Rows.Add(dataRow);
                dataRow.AcceptChanges();
            }
            //Création du parametre SQL
            var tableParameter = new SqlParameter("tablets", dataTable);
            tableParameter.TypeName = "TalbetType";
            tableParameter.SqlDbType = SqlDbType.Structured;
            //On execute la requête
            return await _context.Database.ExecuteSqlCommandAsync("SaveTablets @tablets", tableParameter);

Benefits:

The advantages of this method are:

  • The data you send to SQLServer is very typed so you don't have to go through the SQL procedure to retrieve the information.

  • All l’import is carried out in a single SQL query (so a single go/get).