Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Problemy z wydajnością parametru wartości tabeli

Jeśli TVP są „zauważalnie wolniejsze” niż inne opcje, najprawdopodobniej nie wdrażasz ich poprawnie.

  1. Nie powinieneś używać DataTable, chyba że Twoja aplikacja ma do tego zastosowanie poza wysyłaniem wartości do TVP. Korzystanie z IEnumerable<SqlDataRecord> Interfejs jest szybszy i zużywa mniej pamięci, ponieważ nie duplikujesz kolekcji w pamięci tylko po to, aby wysłać ją do bazy danych. Mam to udokumentowane w następujących miejscach:
  2. Nie powinieneś używać AddWithValue dla SqlParameter, chociaż nie jest to prawdopodobnie problem z wydajnością. Ale nadal powinno być:

    SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured);
    tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
    
  3. TVP są zmiennymi tabelowymi i jako takie nie prowadzą statystyk. Oznacza to, że do Optymalizatora zapytań zgłaszają tylko jeden wiersz. Tak więc w proc, albo:
    • Użyj rekompilacji na poziomie instrukcji dla dowolnych zapytań używających TVP do czegokolwiek innego niż prosty SELECT:OPTION (RECOMPILE)
    • Utwórz lokalną tabelę tymczasową (np. pojedynczy # ) i skopiuj zawartość TVP do tabeli tymczasowej
    • Możesz spróbować dodać klastrowany klucz podstawowy do typu tabeli zdefiniowanej przez użytkownika
    • Jeśli używasz programu SQL Server 2014 lub nowszego, możesz spróbować użyć tabel OLTP w pamięci/zoptymalizowanych pod kątem pamięci. Zobacz:Szybsza tabela temp i zmienna tabeli dzięki optymalizacji pamięci

Jeśli chodzi o to, dlaczego widzisz:

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

zamiast:

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

JEŚLI tak właśnie się dzieje, to:

  • Jeśli wstawki są wykonywane w ramach transakcji, nie ma rzeczywistej różnicy w wydajności
  • Nowsza składnia listy wartości (tj. VALUES (row1), (row2), (row3) ) jest ograniczone do około 1000 wierszy, a zatem nie jest to opłacalna opcja dla TVP, które nie mają tego limitu. JEDNAK prawdopodobnie nie jest to powód, dla którego używane są poszczególne wstawki, biorąc pod uwagę, że nie ma ograniczeń podczas wykonywania INSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col]) , który udokumentowałem tutaj:Maksymalna liczba wierszy dla konstruktora wartości tabeli . Zamiast tego...
  • Powodem jest najprawdopodobniej to, że wykonanie pojedynczych wstawek umożliwia przesyłanie strumieniowe wartości z kodu aplikacji do SQL Server:
    1. za pomocą iteratora (tj. IEnumerable<SqlDataRecord> zauważono w punkcie 1 powyżej), kod aplikacji wysyła każdy wiersz, gdy jest on zwracany z metody, i
    2. konstruowanie VALUES (), (), ... listy, nawet jeśli robisz INSERT INTO ... SELECT FROM (VALUES ...) podejście (które nie jest ograniczone do 1000 wierszy), które nadal wymagałoby zbudowania całości VALUES lista przed wysłaniem dowolnego danych do SQL Server. Jeśli jest dużo danych, zbudowanie bardzo długiego łańcucha zajęłoby więcej czasu i zajęłoby to znacznie więcej pamięci.

Zapoznaj się również z tym dokumentem zespołu doradczego programu SQL Server dla klientów:Maksymalizacja przepustowości dzięki TVP



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wyższy wynik zapytania ze słowem kluczowym DISTINCT?

  2. Jak porównać wartości Null z kolumny bazy danych

  3. Jak włączyć wszystkie ograniczenia wyboru i klucza obcego dla tabeli w SQL Server (przykłady T-SQL)

  4. Przechowywanie dynamicznego wyniku Pivot w tabeli tymczasowej w SQL Server

  5. SPRAWDŹ OGRANICZENIE na wielu kolumnach