sql >> Databasteknik >  >> RDS >> Sqlserver

Problem med tabellvärdesparameterprestanda

Om TVP:er är "märkbart långsammare" än de andra alternativen, är det troligt att du inte implementerar dem korrekt.

  1. Du bör inte använda en DataTable, såvida inte din applikation har användning för det förutom att skicka värdena till TVP. Använda IEnumerable gränssnittet är snabbare och använder mindre minne eftersom du inte duplicerar samlingen i minnet bara för att skicka den till DB. Jag har detta dokumenterat på följande platser:
  2. Du bör inte använda AddWithValue för SqlParameter, även om detta troligen inte är ett prestandaproblem. Men ändå borde det vara:

    SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured);
    tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
    
  3. TVP:er är tabellvariabler och upprätthåller som sådana ingen statistik. Det betyder att de bara rapporterar att de har en rad till frågeoptimeraren. Så, i din proc, antingen:
    • Använd omkompilering på satsnivå på alla frågor som använder TVP för något annat än en enkel SELECT:OPTION (RECOMPILE)
    • Skapa en lokal temporär tabell (dvs. enstaka # ) och kopiera innehållet i TVP till temptabellen
    • Du kan försöka lägga till en klustrad primärnyckel till den användardefinierade tabelltypen
    • Om du använder SQL Server 2014 eller senare kan du prova att använda In-Memory OLTP/minnesoptimerade tabeller. Se:Snabbare temptabell och tabellvariabel genom att använda minnesoptimering

Angående varför du ser:

insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )

istället för:

insert into @data ( ... fields ... ) 
values ( ... values ... ),
       ( ... values ... ),

OM det verkligen är vad som händer, då:

  • Om infogningen görs inom en transaktion finns det ingen verklig prestandaskillnad
  • Den nyare värdelistsyntaxen (dvs. VÄRDEN (rad1), (rad2), (rad3) ) är begränsad till något som 1000 rader och därför inte ett gångbart alternativ för TVP som inte har den gränsen. Detta är dock inte troligt anledningen till att enskilda inlägg används, med tanke på att det inte finns någon gräns när man gör INSERT INTO @data (fält) SELECT-fliken.[col] FROM (VALUES (), (), .. .) tab([col]) , som jag dokumenterade här:Maximalt antal rader för tabellvärdekonstruktören . Istället...
  • Anledningen är mest troligt att genom att göra individuella infogningar kan värdena strömmas från appkoden till SQL Server:
    1. med en iterator (dvs. IEnumerable noteras i #1 ovan), skickar appkoden varje rad när den returneras från metoden, och
    2. konstruerar VALUES (), (), ... listan, även om du gör INSERT INTO ... SELECT FROM (VALUES ...) tillvägagångssätt (som inte är begränsat till 1000 rader), som fortfarande skulle kräva att hela VÄRDEN lista innan du skickar någon av data till SQL Server. Om det finns mycket data skulle det ta längre tid att konstruera den superlånga strängen, och det skulle ta upp mycket mer minne när du gör det.

Se även detta whitepaper från SQL Server Customer Advisory Team:Maximera genomströmning med TVP



  1. mysql-fråga join/inner join

  2. Hur man mappar flera partitioner till en enda filgrupp i SQL Server (T-SQL)

  3. Entity Framework &Oracle:Kan inte infoga VARCHAR2> 1 999 tecken

  4. Passar PostgreSQL:s Ltree-modul bra för gängade kommentarer?