Ако TVP са „забележимо по-бавни“ от другите опции, тогава най-вероятно не ги прилагате правилно.
- Не трябва да използвате DataTable, освен ако приложението ви го използва извън изпращането на стойностите към TVP. Използване на
IEnumerable<SqlDataRecord>
интерфейсът е по-бърз и използва по-малко памет, тъй като не дублирате колекцията в паметта само за да я изпратите в DB. Имам това документирано на следните места:- Как мога да вмъкна 10 милиона записа за възможно най-кратко време? (тук има и много допълнителна информация и връзки)
- Предаване на речник към съхранена процедура T-SQL
- Поточно предаване на данни В SQL Server 2008 от приложение (на SQLServerCentral.com; изисква се безплатна регистрация)
-
Не трябва да използвате
AddWithValue
за SqlParameter, въпреки че това вероятно не е проблем с производителността. Но все пак трябва да е:SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured); tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
- TVP са таблични променливи и като такива не поддържат статистика. Което означава, че те съобщават, че имат само 1 ред на оптимизатора на заявки. Така че във вашата процедура или:
- Използвайте прекомпилиране на ниво израз на всякакви заявки, използващи TVP за нещо различно от обикновен SELECT:
OPTION (RECOMPILE)
- Създайте локална временна таблица (т.е. единичен
#
) и копирайте съдържанието на TVP във временната таблица - Можете да опитате да добавите клъстерен първичен ключ към дефинирания от потребителя тип таблица
- Ако използвате SQL Server 2014 или по-нов, можете да опитате да използвате In-Memory OLTP / таблици, оптимизирани за памет. Моля, вижте:По-бърза временна таблица и променлива на таблица чрез използване на оптимизация на паметта
- Използвайте прекомпилиране на ниво израз на всякакви заявки, използващи TVP за нещо различно от обикновен SELECT:
Относно защо виждате:
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )
вместо:
insert into @data ( ... fields ... )
values ( ... values ... ),
( ... values ... ),
АКО това наистина се случва, тогава:
- Ако вмъкванията се извършват в рамките на транзакция, тогава няма реална разлика в производителността
- По-новият синтаксис на списък със стойности (т.е.
VALUES (row1), (row2), (row3)
) е ограничен до нещо като 1000 реда и следователно не е жизнеспособна опция за TVP, които нямат това ограничение. ОБАЧЕ, това не е вероятно причината да се използват отделни вмъквания, като се има предвид, че няма ограничение, когато правитеINSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col])
, което документирах тук:Максимален брой редове за конструктора на стойността на таблицата . Вместо това... - Причината най-вероятно е, че извършването на отделни вмъквания позволява поточно предаване на стойностите от кода на приложението в SQL Server:
- използване на итератор (т.е.
IEnumerable<SqlDataRecord>
отбелязано в #1 по-горе), кодът на приложението изпраща всеки ред, както е върнат от метода, и - конструиране на
VALUES (), (), ...
списък, дори ако правитеINSERT INTO ... SELECT FROM (VALUES ...)
подход (който не е ограничен до 1000 реда), който пак ще изисква изграждане на цялатаVALUES
списък, преди да изпратите всяко на данните в SQL Server. Ако има много данни, това ще отнеме повече време за конструиране на супер дългия низ и ще заеме много повече памет, докато го прави.
- използване на итератор (т.е.
Моля, вижте също тази бяла книга от екипа за консултации на клиенти на SQL Server:Максимизиране на пропускателната способност с TVP