Pokud jsou TVP "znatelně pomalejší" než ostatní možnosti, pak je s největší pravděpodobností neimplementujete správně.
- Neměli byste používat DataTable, pokud to vaše aplikace nepoužívá mimo odesílání hodnot do TVP. Pomocí
IEnumerable<SqlDataRecord>
rozhraní je rychlejší a využívá méně paměti, protože neduplikujete kolekci v paměti pouze za účelem jejího odeslání do DB. Mám to zdokumentované na následujících místech:- Jak mohu vložit 10 milionů záznamů v co nejkratším čase? (zde také spousta dalších informací a odkazů)
- Předat slovník do uložené procedury T-SQL
- Streamování dat Do SQL Server 2008 z aplikace (na SQLServerCentral.com; nutná bezplatná registrace)
-
Neměli byste používat
AddWithValue
pro SqlParameter, i když to pravděpodobně není problém s výkonem. Ale přesto by to mělo být:SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured); tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
- TVP jsou proměnné tabulky a jako takové nevedou statistiky. To znamená, že mají pouze 1 řádek pro optimalizaci dotazů. Takže ve vašem procesu buď:
- Použijte rekompilaci na úrovni příkazů pro jakékoli dotazy pomocí TVP pro cokoli jiného než jednoduchý SELECT:
OPTION (RECOMPILE)
- Vytvořte místní dočasnou tabulku (tj. jeden
#
) a zkopírujte obsah TVP do dočasné tabulky - Můžete zkusit přidat seskupený primární klíč do uživatelem definovaného typu tabulky
- Pokud používáte SQL Server 2014 nebo novější, můžete zkusit použít In-Memory OLTP / tabulky optimalizované pro paměť. Viz:Rychlejší dočasná tabulka a proměnná tabulky pomocí optimalizace paměti
- Použijte rekompilaci na úrovni příkazů pro jakékoli dotazy pomocí TVP pro cokoli jiného než jednoduchý SELECT:
O tom, proč se vám zobrazuje:
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )
místo:
insert into @data ( ... fields ... )
values ( ... values ... ),
( ... values ... ),
POKUD se to skutečně děje, pak:
- Pokud se vkládání provádí v rámci transakce, neexistuje žádný skutečný rozdíl ve výkonu
- Novější syntaxe seznamu hodnot (tj.
VALUES (row1), (row2), (row3)
) je omezena na něco jako 1000 řádků, a proto není životaschopnou možností pro TVP, které tento limit nemají. OVŠEM to není pravděpodobně důvod, proč se používají jednotlivé vložky, vzhledem k tomu, že neexistuje žádné omezení při prováděníINSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col])
, kterou jsem zdokumentoval zde:Maximální počet řádků pro konstruktor hodnot tabulky . Místo toho... - Důvodem je s největší pravděpodobností to, že provádění jednotlivých vložení umožňuje streamování hodnot z kódu aplikace na SQL Server:
- pomocí iterátoru (tj.
IEnumerable<SqlDataRecord>
jak je uvedeno v bodě 1 výše), kód aplikace odešle každý řádek tak, jak je vrácen z metody, a - vytváření
VALUES (), (), ...
seznam, i když provedeteINSERT INTO ... SELECT FROM (VALUES ...)
přístup (který není omezen na 1000 řádků), který by stále vyžadoval vytvoření celéhoVALUES
seznam před odesláním jakéhokoli dat na SQL Server. Pokud existuje mnoho dat, vytvoření superdlouhého řetězce by trvalo déle a zabralo by to mnohem více paměti.
- pomocí iterátoru (tj.
Podívejte se také na tento dokument od poradenského týmu pro zákazníky SQL Server:Maximalizace propustnosti pomocí TVP