Hvis TVP'er er "mærkbart langsommere" end de andre muligheder, er det højst sandsynligt, at du ikke implementerer dem korrekt.
- Du bør ikke bruge en datatabel, medmindre din applikation har brug for det uden for at sende værdierne til TVP. Brug
IEnumerable<SqlDataRecord>
grænsefladen er hurtigere og bruger mindre hukommelse, da du ikke duplikerer samlingen i hukommelsen kun for at sende den til DB. Jeg har dette dokumenteret følgende steder:- Hvordan kan jeg indsætte 10 millioner poster på kortest mulig tid? (masser af ekstra info og links også her)
- Giv ordbog til Stored Procedure T-SQL
- Streaming data Ind i SQL Server 2008 fra en applikation (på SQLServerCentral.com; gratis registrering påkrævet)
-
Du bør ikke bruge
AddWithValue
for SqlParameter, selvom dette sandsynligvis ikke er et ydeevneproblem. Men alligevel skulle det være:SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured); tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
- TVP'er er tabelvariabler og vedligeholder som sådan ikke statistik. Det betyder, at de kun rapporterer at have 1 række til Query Optimizer. Så i din proc, enten:
- Brug omkompilering på sætningsniveau på alle forespørgsler, der bruger TVP til alt andet end en simpel SELECT:
OPTION (RECOMPILE)
- Opret en lokal midlertidig tabel (dvs. enkelt
#
) og kopier indholdet af TVP'en ind i temp-tabellen - Du kan prøve at tilføje en klynget primærnøgle til den brugerdefinerede tabeltype
- Hvis du bruger SQL Server 2014 eller nyere, kan du prøve at bruge In-Memory OLTP/hukommelsesoptimerede tabeller. Se venligst:Hurtigere temperaturtabel og tabelvariabel ved at bruge hukommelsesoptimering
- Brug omkompilering på sætningsniveau på alle forespørgsler, der bruger TVP til alt andet end en simpel SELECT:
Med hensyn til hvorfor du ser:
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )
i stedet for:
insert into @data ( ... fields ... )
values ( ... values ... ),
( ... values ... ),
HVIS det faktisk er det, der sker, så:
- Hvis indsættelserne udføres inden for en transaktion, er der ingen reel forskel i ydeevne
- Den nyere værdilistesyntaks (dvs.
VALUES (row1), (row2), (row3)
) er begrænset til noget i retning af 1000 rækker og er derfor ikke en levedygtig mulighed for TVP'er, der ikke har den grænse. DOG er dette ikke sandsynligt årsagen til, at individuelle inserts bliver brugt, da der ikke er nogen grænse, når du laverINSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col])
, som jeg dokumenterede her:Maksimalt antal rækker for tabelværdikonstruktøren . I stedet... - Årsagen er højst sandsynligt, at individuelle indsættelser giver mulighed for at streame værdierne fra appkoden til SQL Server:
- ved at bruge en iterator (dvs. den
IEnumerable<SqlDataRecord>
noteret i #1 ovenfor), sender appkoden hver række, efterhånden som den returneres fra metoden, og - konstruerer
VALUES (), (), ...
liste, selvom du gørINSERT INTO ... SELECT FROM (VALUES ...)
tilgang (som ikke er begrænset til 1000 rækker), som stadig vil kræve opbygning af heleVALUES
liste, før du sender enhver af dataene til SQL Server. Hvis der er mange data, ville det tage længere tid at konstruere den superlange streng, og det ville optage meget mere hukommelse, mens du gør det.
- ved at bruge en iterator (dvs. den
Se også denne hvidbog fra SQL Server Customer Advisory Team:Maksimering af gennemløb med TVP