sql >> Database >  >> RDS >> Sqlserver

Problemen met de prestaties van tabelwaardeparameters

Als TVP's "aanzienlijk langzamer" zijn dan de andere opties, implementeert u ze hoogstwaarschijnlijk niet correct.

  1. U zou geen DataTable moeten gebruiken, tenzij uw toepassing er gebruik voor heeft buiten het verzenden van de waarden naar de TVP. De IEnumerable<SqlDataRecord> . gebruiken interface is sneller en gebruikt minder geheugen omdat u de verzameling niet in het geheugen dupliceert om deze naar de database te verzenden. Ik heb dit gedocumenteerd op de volgende plaatsen:
  2. Gebruik AddWithValue niet voor de SqlParameter, hoewel dit waarschijnlijk geen prestatieprobleem is. Maar toch, het zou moeten zijn:

    SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured);
    tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
    
  3. TVP's zijn tabelvariabelen en houden als zodanig geen statistieken bij. Dit betekent dat ze rapporteren dat ze slechts 1 rij hebben aan de Query Optimizer. Dus, in je proces, ofwel:
    • Gebruik opnieuw compileren op instructieniveau voor alle query's die de TVP gebruiken voor iets anders dan een simpele SELECT:OPTION (RECOMPILE)
    • Maak een lokale tijdelijke tabel (d.w.z. enkele # ) en kopieer de inhoud van de TVP naar de tijdelijke tabel
    • U kunt proberen een geclusterde primaire sleutel toe te voegen aan het door de gebruiker gedefinieerde tabeltype
    • Als u SQL Server 2014 of nieuwer gebruikt, kunt u proberen gebruik te maken van In-Memory OLTP / voor geheugen geoptimaliseerde tabellen. Zie:Sneller tijdelijke tabel en tabelvariabele door gebruik te maken van geheugenoptimalisatie

Met betrekking tot waarom je ziet:

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

in plaats van:

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

ALS dat werkelijk is wat er gebeurt, dan:

  • Als de invoegingen binnen een transactie worden gedaan, is er geen echt prestatieverschil
  • De nieuwere syntaxis van de waardelijst (d.w.z. VALUES (row1), (row2), (row3) ) is beperkt tot ongeveer 1000 rijen en is daarom geen haalbare optie voor TVP's die die limiet niet hebben. ECHTER, dit is niet waarschijnlijk de reden dat individuele invoegingen worden gebruikt, aangezien er geen limiet is bij het uitvoeren van INSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col]) , die ik hier heb gedocumenteerd:Maximum aantal rijen voor de tabelwaardeconstructor . In plaats daarvan...
  • De reden is hoogstwaarschijnlijk dat het doen van individuele invoegingen het mogelijk maakt om de waarden van de app-code naar SQL Server te streamen:
    1. met behulp van een iterator (d.w.z. de IEnumerable<SqlDataRecord> vermeld in #1 hierboven), verzendt de app-code elke rij zoals deze wordt geretourneerd door de methode, en
    2. het construeren van de VALUES (), (), ... lijst, zelfs als u de INSERT INTO ... SELECT FROM (VALUES ...) benadering (die niet beperkt is tot 1000 rijen), waarvoor nog steeds de gehele . moet worden gebouwd VALUES lijst voordat u elke . verzendt van de gegevens in SQL Server. Als er veel gegevens zijn, zou het langer duren om de superlange reeks te construeren en zou het veel meer geheugen in beslag nemen.

Zie ook deze whitepaper van het SQL Server Customer Advisory Team:Maximale doorvoer met TVP



  1. 3 manieren om de maandnaam van een datum in MariaDB te krijgen

  2. MySQL:InnoDb:Semaphore-wachttijd heeft> 600 seconden geduurd. We laten de server opzettelijk crashen

  3. SQLAlchemy OP DUPLICATE KEY UPDATE

  4. Voeg meerdere rijen toe in slechts één rij vanuit een enkele tabel