Jika TVP "terasa lebih lambat" daripada opsi lainnya, kemungkinan besar Anda tidak menerapkannya dengan benar.
- Anda tidak boleh menggunakan DataTable, kecuali aplikasi Anda menggunakannya di luar pengiriman nilai ke TVP. Menggunakan
IEnumerable<SqlDataRecord>
antarmuka lebih cepat dan menggunakan lebih sedikit memori karena Anda tidak menduplikasi koleksi dalam memori hanya untuk mengirimkannya ke DB. Saya telah mendokumentasikan ini di tempat-tempat berikut:- Bagaimana cara memasukkan 10 juta catatan dalam waktu sesingkat mungkin? (banyak info dan tautan tambahan di sini juga)
- Meneruskan Kamus ke T-SQL Prosedur Tersimpan
- Streaming Data Ke SQL Server 2008 Dari Aplikasi (di SQLServerCentral.com; pendaftaran gratis diperlukan)
-
Anda tidak boleh menggunakan
AddWithValue
untuk SqlParameter, meskipun ini sepertinya bukan masalah kinerja. Tapi tetap harus:SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured); tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
- TVP adalah Variabel Tabel dan karena itu tidak memelihara statistik. Artinya, mereka melaporkan hanya memiliki 1 baris ke Query Optimizer. Jadi, di proc Anda, baik:
- Gunakan kompilasi ulang tingkat pernyataan pada kueri apa pun menggunakan TVP untuk apa pun selain SELECT sederhana:
OPTION (RECOMPILE)
- Buat tabel sementara lokal (yaitu satu
#
) dan salin konten TVP ke tabel temp - Anda dapat mencoba menambahkan kunci utama yang dikelompokkan ke Jenis Tabel Buatan Pengguna
- Jika menggunakan SQL Server 2014 atau yang lebih baru, Anda dapat mencoba menggunakan tabel In-Memory OLTP/memori yang dioptimalkan. Silakan lihat:Tabel temp dan variabel tabel lebih cepat dengan menggunakan optimasi memori
- Gunakan kompilasi ulang tingkat pernyataan pada kueri apa pun menggunakan TVP untuk apa pun selain SELECT sederhana:
Mengenai mengapa Anda melihat:
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )
bukannya:
insert into @data ( ... fields ... )
values ( ... values ... ),
( ... values ... ),
JIKA memang itu yang terjadi, maka:
- Jika penyisipan dilakukan dalam suatu Transaksi maka tidak ada perbedaan kinerja yang nyata
- Sintaks daftar nilai yang lebih baru (yaitu
VALUES (row1), (row2), (row3)
) terbatas pada sekitar 1000 baris dan karenanya bukan opsi yang layak untuk TVP yang tidak memiliki batas itu. NAMUN, ini sepertinya bukan alasan penggunaan sisipan individual, mengingat tidak ada batasan saat melakukan tabINSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col])
, yang saya dokumentasikan di sini:Jumlah Maksimum Baris untuk Konstruktor Nilai Tabel . Sebaliknya... - Alasannya kemungkinan besar karena melakukan penyisipan individual memungkinkan streaming nilai dari kode aplikasi ke SQL Server:
- menggunakan iterator (yaitu
IEnumerable<SqlDataRecord>
disebutkan di #1 di atas), kode aplikasi mengirimkan setiap baris seperti yang dikembalikan dari metode, dan - membuat
VALUES (), (), ...
list, bahkan jika melakukanINSERT INTO ... SELECT FROM (VALUES ...)
pendekatan (yang tidak terbatas pada 1000 baris), yang masih memerlukan pembuatan seluruhVALUES
daftar sebelum mengirim apa saja dari data ke SQL Server. Jika ada banyak data, itu akan memakan waktu lebih lama untuk membuat string yang sangat panjang, dan akan memakan lebih banyak memori saat melakukannya.
- menggunakan iterator (yaitu
Silakan lihat juga whitepaper ini dari Tim Penasihat Pelanggan SQL Server:Memaksimalkan Throughput dengan TVP