MSSQL 2017 - эффективная вставка больших наборов данных

Я пытаюсь вставить большой набор данных (около миллиона строк) из файла CSV с помощью приложения C# в базу данных с четырьмя таблицами. В файле CSV около 25% строк неверны из-за дублирования первичных ключей, и каждая строка разбивается на четыре таблицы. Вставка строки из CSV выполняется четырьмя процедурами (каждая для одной таблицы), упакованными в транзакцию с помощью оператора try-catch. Вставка 10000 строк занимает около одной минуты, так что это слишком долго. Существует ли более быстрый способ вставить большой набор данных и постоянно проверять целостность данных? Спасибо.

Обновлено: вот код моих процедур


USE semestralka
GO

CREATE PROCEDURE sp_vloz_ridice @crp nchar(9), @jmeno varchar(50)
AS
BEGIN
    IF NOT EXISTS
        (SELECT crp, jmeno FROM ridici 
            WHERE crp = @crp AND jmeno = @jmeno)
    INSERT INTO ridici (crp, jmeno)
    VALUES (@crp, @jmeno)
END

USE semestralka
GO

CREATE PROCEDURE sp_vloz_auto @SPZ nchar(8), @barva int, @vyrobce nvarchar(25), @model nvarchar(40)
AS
BEGIN
    IF NOT EXISTS
        (SELECT SPZ, barva, vyrobce, model FROM auta 
            WHERE SPZ = @SPZ AND barva = @barva AND vyrobce = @vyrobce AND model = @model)
    INSERT INTO auta (SPZ, barva, vyrobce, model)
    VALUES (@SPZ, @barva, @vyrobce, @model)
END

USE semestralka
GO

CREATE PROCEDURE sp_vloz_branu @brana_jmeno nchar(10), @typ varchar(10), @cena real, @gps_lattitude real, @gps_longtitude real
AS
BEGIN
    IF NOT EXISTS
        (SELECT jmeno, typ, cena, gps_lattitude, gps_longtitude  FROM brany
            WHERE jmeno = @brana_jmeno AND typ = @typ AND cena = @cena AND gps_lattitude = @gps_lattitude AND gps_longtitude = @gps_longtitude )
    INSERT INTO brany (jmeno, typ, cena, gps_lattitude, gps_longtitude)
    VALUES (@brana_jmeno, @typ, @cena, @gps_lattitude, @gps_longtitude)
END

USE semestralka
GO

CREATE PROCEDURE sp_vloz_prujezd @prujezd_datum_cas int, @fk_prujezd_spz nchar(8), @fk_prujezd_crp nchar(9), @fk_gps_lattitude real, @fk_gps_longtitude real, @tachometr_stav int, @palivo_stav real, @napeti_baterie real
AS
BEGIN
    INSERT INTO prujezdy (prujezd_datum_cas, fk_prujezd_spz, fK_prujezd_crp, fk_gps_lattitude, fk_gps_longtitude, tachometr_stav, palivo_stav, napeti_baterie)
    VALUES (@prujezd_datum_cas, @fk_prujezd_spz, @fK_prujezd_crp,  @fk_gps_lattitude, @fk_gps_longtitude, @tachometr_stav, @palivo_stav, @napeti_baterie)
END

USE semestralka
GO

CREATE PROCEDURE sp_super_insert @SPZ nchar(8), @barva int, @vyrobce nvarchar(25), @model nvarchar(40),
                                @crp nchar(9), @jmeno varchar(50),
                                @brana_jmeno nchar(10), @typ varchar(10), @cena real, @gps_lattitude real, @gps_longtitude real,
                                @prujezd_datum_cas int, @tachometr_stav int, @palivo_stav real, @napeti_baterie real, @output bit OUTPUT
AS
BEGIN
    SET @output = 0
    BEGIN TRANSACTION
    BEGIN TRY
        EXEC sp_vloz_auto @SPZ, @barva, @vyrobce, @model
        EXEC sp_vloz_ridice @crp, @jmeno
        EXEC sp_vloz_branu @brana_jmeno, @typ, @cena, @gps_lattitude, @gps_longtitude
        EXEC sp_vloz_prujezd @prujezd_datum_cas, @SPZ, @crp, @gps_lattitude, @gps_longtitude, @tachometr_stav, @palivo_stav, @napeti_baterie
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION
        RETURN @output
    END CATCH
    COMMIT TRANSACTION
    SET @output = 1
    RETURN @output
END

Быстрее чем? Вы не сказали нам, что делают эти хранимые процедуры. Покажите нам свой код.

Tab Alleman 08.05.2018 15:35

Вы пробовали SSIS? Правильный сервис ETL, вероятно, был бы «лучше», однако @TabAlleman прямо здесь, нам не с чем сейчас работать.

Larnu 08.05.2018 15:40

Вы можете использовать класс SQLBulkCopy. Вы можете установить для него KeepIdentity как часть аргумента bulkcopyoptions, но я считаю, что другая проверка данных должна выполняться отдельно. Вам нужно будет построить сопоставление столбцов перед вставкой, если ваши спецификации столбцов не совпадают. Это настоящий проект для SO-поста.

Jacob H 08.05.2018 15:58

Другой вариант - выполнить массовую вставку хранимого процесса SQL Server в промежуточную таблицу, а затем (1) проверить данные и (2) запустить операторы insert into...select from из промежуточной таблицы. Однако SSIS предпочтительнее, если вы можете им управлять.

anu start 08.05.2018 16:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
150
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используйте операции на основе наборов вместо построчных операторов (или курсора / foreach). В настоящее время вы выполняете хранимую процедуру, которая выполняет еще 4 хранимых процедуры внутри транзакции, просто чтобы вставить 4 строки в 4 разные таблицы. Выполнение этого тысячи раз чрезвычайно медленно по сравнению с базовым решением, в котором вы выполняете все строки одновременно, не жертвуя своими бизнес-правилами.

Используйте команду Bulk Insert (из вашего приложения C#) в промежуточную таблицу, которая представляет собой таблицу для хранения временных данных перед их вставкой в ​​ваши конечные таблицы. В вашем случае у вас будет 4 разных промежуточных стола.

Эти промежуточные таблицы не будут иметь ограничений, ключей, триггеров или любого другого механизма, который может задержать операцию вставки. Массовые вставки на таких столах работают очень быстро.

После того, как вы вставите свой CSV в промежуточные таблицы, вы можете использовать SQL для проверки и вставки записей в конечную таблицу (на основе набора, а не 1 на 1). Вы можете создавать или включать (перестраивать) индексы в промежуточных таблицах, если вам нужно выполнить объединения или определенные фильтры для определенных столбцов.

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

Спасибо. Это решение намного быстрее.

Matěj Řehák 14.05.2018 11:01

Другие вопросы по теме