Подтвердить что ты не робот

Производительность вставки SQL Server

У меня есть запрос на вставку, который создается таким образом

INSERT INTO InvoiceDetail (LegacyId,InvoiceId,DetailTypeId,Fee,FeeTax,Investigatorid,SalespersonId,CreateDate,CreatedById,IsChargeBack,Expense,RepoAgentId,PayeeName,ExpensePaymentId,AdjustDetailId) 
VALUES(1,1,2,1500.0000,0.0000,163,1002,'11/30/2001 12:00:00 AM',1116,0,550.0000,850,NULL,@ExpensePay1,NULL); 
DECLARE @InvDetail1 INT; SET @InvDetail1 = (SELECT @@IDENTITY);

Этот запрос генерируется только для строк 110K.

Для выполнения всех этих запросов требуется 30 минут

Я проверил план запроса, и наибольшие% узлов

Кластеризованный индекс Вставка при 57% стоимости запроса который имеет длинный xml, который я не хочу публиковать.

Столовая катушка, которая составляет 38% стоимости запроса

<RelOp AvgRowSize="35" EstimateCPU="5.01038E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Eager Spool" NodeId="80" Parallel="false" PhysicalOp="Table Spool" EstimatedTotalSubtreeCost="0.0466109">
  <OutputList>
    <ColumnReference Database="[SkipPro]" Schema="[dbo]" Table="[InvoiceDetail]" Column="InvoiceId" />
    <ColumnReference Database="[SkipPro]" Schema="[dbo]" Table="[InvoiceDetail]" Column="InvestigatorId" />
    <ColumnReference Column="Expr1054" />
    <ColumnReference Column="Expr1055" />
  </OutputList>
  <Spool PrimaryNodeId="3" />
</RelOp>

Итак, мой вопрос - что я могу сделать, чтобы улучшить скорость этой вещи? Я уже запускал ALTER TABLENAME NOCHECK ОГРАНИЧИВАЕТ ВСЕ Перед запросами, а затем ALTER TABLENAME NOCHECK ОГРАНИЧИВАЕТ ВСЕ после запросов.

И это не сбривало ничего с того времени.

Знаю, что я запускаю эти запросы в приложении .NET, которое использует объект SqlCommand для отправки запроса.

Затем я попытался вывести команды sql в файл, а затем выполнить его с помощью sqlcmd, но я не получал никаких обновлений о том, как это происходит, поэтому я отказался от этого.

Любые идеи или подсказки или помощь?

ОБНОВЛЕНИЕ:

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

Решение исправить это было двояким.

Первое:

1) Я отключил/повторно включил все внешние ключи (намного проще, чем их сбросить)

ALTER TABLE TableName NOCHECK CONSTRAINT ALL
ALTER TABLE TableName CHECK CONSTRAINT ALL

2) Я отключил/снова установил индексы (снова намного проще, чем сбросить)

ALTER INDEX [IX_InvoiceDetail_1] ON [dbo].[InvoiceDetail] DISABLE
ALTER INDEX [IX_InvoiceDetail_1] ON [dbo].[InvoiceDetail] REBUILD PARTITION = ALL WITH ( PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, ONLINE = OFF, SORT_IN_TEMPDB = OFF )

Второе:

Я завернул все операторы вставки в одну транзакцию. Я изначально не знал, как это сделать в .NET.

Я очень ценю весь вход, который я получил.

Если я когда-либо сделаю такой перевод из БД в БД, я обязательно начну с BULK INSERT. Это кажется намного более гибким и быстрым.

4b9b3361

Ответ 1

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

Ответ 2

Выполняете ли вы эти запросы по одному от клиента .NET(т.е. отправляете 110 000 отдельных запросов запросов на SQL Server)?

В этом случае вероятно, что это латентность сети и другие накладные расходы на отправку этих ВСТАВКИ на SQL Server без их пакетной обработки, а не сам SQL Server.

Отметьте BULK INSERT.

Ответ 3

Скорее всего, это фиксация флеша. Если вы не переносите наборы INSERT в транзакцию с явным управлением, каждый INSERT является своей собственной транзакцией с автоматическим подтверждением. Значение каждого INSERT автоматически фиксирует фиксацию, и фиксация должна ждать, пока журнал будет долговечен (т.е. записан на диск). Промывка журнала после каждой вставки очень медленная.

Например, пытаясь вставить 100k строк, подобных вашим, в стиле фиксации одной строки:

set nocount on; 
declare @start datetime = getutcdate();  

declare @i int = 0;
while @i < 100000
begin
INSERT INTO InvoiceDetail (
  LegacyId,InvoiceId,DetailTypeId,Fee,
  FeeTax,Investigatorid,SalespersonId,
  CreateDate,CreatedById,IsChargeBack,
  Expense,RepoAgentId,PayeeName,ExpensePaymentId,
  AdjustDetailId) 
  VALUES(1,1,2,1500.0000,0.0000,163,1002,
    '11/30/2001 12:00:00 AM',
    1116,0,550.0000,850,NULL,1,NULL); 
  set @i = @i+1;
end

select datediff(ms, @start, getutcdate());

Это выполняется примерно через 12 секунд на моем сервере. Но добавив управление транзакциями и совершая каждые 1000 строк, вставка строк 100 тыс. Длится всего около 4 секунд:

set nocount on;  
declare @start datetime = getutcdate();  

declare @i int = 0;
begin transaction
while @i < 100000
begin
INSERT INTO InvoiceDetail (
  LegacyId,InvoiceId,DetailTypeId,
  Fee,FeeTax,Investigatorid,
  SalespersonId,CreateDate,CreatedById,
  IsChargeBack,Expense,RepoAgentId,
  PayeeName,ExpensePaymentId,AdjustDetailId) 
  VALUES(1,1,2,1500.0000,0.0000,163,1002,
    '11/30/2001 12:00:00 AM',
    1116,0,550.0000,850,NULL,1,NULL); 
  set @i = @i+1;
  if (@i%1000 = 0)
  begin
    commit
    begin transaction
  end  
end
commit;
select datediff(ms, @start, getutcdate());

Также, учитывая, что я могу вставить 100 тыс. строк за 12 секунд даже без коммита, в то время как вам нужно 30 минут, стоит оценить 1) скорость вашей подсистемы ввода-вывода (например, что Avg. Sec per Transaction, которое вы видите на диски) и 2) что еще делает код клиента между получением идентификатора @@с одного вызова и вызовом следующей вставки. Может быть, основная часть времени находится на стороне клиента в стеке. Одним простым решением было бы запустить несколько вставок параллельно (BeginExecuteNonQuery), чтобы вы постоянно загружали вставки SQL Server.

Ответ 4

Вы отметили этот вопрос как "bulkinsert". Итак, почему бы не использовать команду BULK INSERT?

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

Ответ 5

Есть несколько вещей, которые вы можете сделать:

1) Disable any triggers on this table
2) Drop all indexes
3) Drop all foreign keys
4) Disable any check constraints

Ответ 6

Запуск отдельных INSERT всегда будет самым медленным вариантом. Кроме того, какая сделка с @@IDENTITY - не похожа на то, что вы отслеживали тех, кто находится между ними.

Если вы не хотите использовать BULK INSERT из файла или SSIS, в ADO.NET есть функция SqlBulkCopy, которая, вероятно, сделайте все возможное, если вам абсолютно необходимо сделать это из .NET-программы.

Строки 110k должны занимать меньше времени для импорта, чем при повторном подключении и написании этого ответа.

Ответ 7

Некоторые предложения по увеличению производительности вставки:

  • Увеличить пакет ADO.NET BatchSize
  • Удобно использовать кластеризованный индекс целевой таблицы, чтобы вставки не приводили к кластерному индексу node splits (например, столбцу autoinc)
  • Сначала введите во временную таблицу кучи, затем введите один большой оператор "вставить-выберете", чтобы переместить все данные промежуточной таблицы в фактическую таблицу целей.
  • Применить SqlBulkCopy
  • Поместите блокировку таблицы перед вставкой (если это позволяет ваш бизнес-сценарий)

Взято из Советы по быстрому встраиванию встраивания на SqlServer

Ответ 8

Hm, пусть он запустится, проверьте счетчики производительности. что ты видишь? Какая дисковая компоновка у вас есть? Я могу вставить миллион строк за 30 минут - точнее, сто миллионов миллиметров строк (финансовая информация в реальном времени, ссылки на 3 другие таблицы). Я почти уверен, что ваш макет ввода-вывода плох (т.е. Плохая структура диска, плохое распределение файлов)