Как более эффективно сохранять массовые записи как транзакции на SQL-сервере? - PullRequest
0 голосов
/ 08 ноября 2018

Я работаю над приложением C # MVC. В этом приложении пользователь загружает данные из таблицы EXCEL, и данные отображаются в сетке.

После того, как он отображается в сетке, пользователь нажимает кнопку «Подтвердить данные». Прикладная программа должна выполнять валидацию пользовательского интерфейса (длина данных, пустое поле, форматы данных и т. Д.), А также, кроме того, требуются валидации SQL, например. запись не должна уже существовать, никаких ограничений и т. д.

После того, как данные проверки отображаются пользователю для любых ошибок, связанных с каждой строкой, чтобы он мог внести исправления в вставленные данные и затем сохранить данные как транзакцию в базе данных сервера SQL.

Один из способов сделать это - зациклить данные в коде C # и выполнить проверки для каждой строки, вызвав некоторую хранимую процедуру с операторами return, затем сохранить те же данные, вероятно, в наборе данных, а затем отобразить их пользователю в сетка. Затем, когда он отправляет, выполняйте операторы вставки в цикле в транзакции.

Проблема в том, что подход, о котором я думаю, удваивает количество обращений к базе данных.

Таким образом, если в сетке 100 строк, это повлечет за собой 200 обращений к базе данных.

Я ищу совета, если есть другой эффективный способ сделать это.

1 Ответ

0 голосов
/ 08 ноября 2018

Вот мой подход:

Вы можете проверить всю свою проверку на стороне пользовательского интерфейса на стороне клиента, например, длину и т. Д., Чтобы вам не нужно было переходить к приложению, а также к серверу базы данных.

Для работы с данными здесь есть подход, который я реализовывал много раз.

  1. Создайте тип таблицы, в котором должны быть все столбцы, которые нужно обработать.

  2. Используйте эту переменную табличного типа в хранимой процедуре в качестве входного параметра, где вы можете передать n-количество строк за один раз, чтобы вам не пришлось зацикливаться на c # для многократного обращения к базе данных.

  3. Оператор слияния пользователя внутри хранимой процедуры, если запись не совпадает, вы можете вставить ее, а при совпадении вы можете обновить ее при необходимости. Вы также можете выполнить это действие внутри транзакции.

Надеюсь, это поможет вам.

РЕДАКТИРОВАТЬ 1: На основе вашего комментария для проверки уровня базы данных.

Было бы два типа ошибок на стороне базы данных. 1. Данные сами по себе не будут в формате, как этого требует определение таблицы SQL, например, при сбое преобразования типов данных.

  1. Ошибка уровня DDL, например, превышение длины данных или ограничения внешнего ключа и т. Д.

Я бы порекомендовал сделать всю возможную проверку данных, которые вы можете кодировать на уровне c #. Как длина данных на основе вашего столбца назначения. Тип данных перед вызовом хранимой процедуры, и вы можете фильтровать такие записи на уровне C #. На этом уровне вы можете делать максимальные проверки.

После передачи данных на сервер sql вы можете использовать , пытаясь поймать на сервере SQL, где вы можете реализовать логику неудачных строк. Сохраните ошибочные строки во временной таблице, которую вы можете вернуть позже, и вставьте все успешно.

РЕДАКТИРОВАТЬ 2: Вот возможный код.

CREATE TABLE Users
(
    Idx             BIGINT IDENTITY(1,1),
    UserID          UNIQUEIDENTIFIER,
    FirstName       VARCHAR(100),
    LastName        VARCHAR(100),
    Email           VARCHAR(100),
    UserPassword    VARCHAR(100),
    InsertDate      DATETIME,
    UpDateDate      DATETIME,
    IsActive        BIT,
 CONSTRAINT [Users_PK] PRIMARY KEY CLUSTERED 
(
    [UserID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

CREATE TYPE UT_Users AS TABLE  
(  
    Idx             INT,
    FirstName       VARCHAR(100),
    LastName        VARCHAR(100),
    Email           VARCHAR(100),
    UserPassword    VARCHAR(100),
    InsertDate      DATETIME,
    UpDateDate      DATETIME,
    IsActive        BIT
) 
GO

CREATE PROCEDURE uspInsertUsers(@user_Details [UT_Users]) READONLY
AS  
BEGIN  
    DECLARE @Counter INT=1
    DECLARE @TotalRows INT=0

    SELECT @TotalRows = COUNT(1) FROM @user_Details

    WHILE @TotalRows>@Counter
        BEGIN  
            TRY
                BEGIN
                    INSERT INTO dbo.Users  
                    SELECT * FROM @user_Details WHERE @Counter = Idx
                END
            CATCH
                BEGIN
                    --write code for catching the error as per your need. Store row in temp tables and return the temp table at the end
                END
            SET @Counter = @Counter+1
        END  
END
GO
DECLARE @user_Details AS [UT_Users];

INSERT @user_Details
SELECT 1,'Rahul','Neekhra','rahul@india.com','12345',GETDATE(),GETDATE(),1 UNION
SELECT 2,'James','Streak','streak@usa.com','12345',GETDATE(),GETDATE(),1 

EXEC uspInsertUsers @user_Details
...