Penjelasannya tampaknya terkait dengan kombinasi dari: a) detail dari blog tertaut yang tidak disebutkan dalam pertanyaan ini, b) pragmatik TVP yang sesuai dengan bagaimana parameter selalu masuk dan keluar, c) dan sifatnya variabel tabel.
Detail yang hilang yang terdapat dalam posting blog tertaut adalah bagaimana tepatnya variabel masuk dan keluar dari Stored Procedures and Functions (yang berkaitan dengan ungkapan dalam Pertanyaan "versi pass-by-reference yang lebih aman jika mereka adalah parameter OUTPUT") :
TSQL menggunakan semantik copy-in / copy-out untuk meneruskan parameter ke prosedur dan fungsi yang tersimpan ....
... ketika proc yang disimpan selesai dieksekusi (tanpa memukul kesalahan) sebuah copy-out dibuat yang memperbarui parameter yang diteruskan dengan perubahan apa pun yang dilakukan padanya dalam proc yang disimpan.
Manfaat nyata dari pendekatan ini adalah dalam kasus kesalahan. Jika kesalahan terjadi di tengah pelaksanaan prosedur tersimpan, setiap perubahan yang dilakukan pada parameter tidak akan merambat kembali ke pemanggil.
Jika kata kunci OUTPUT tidak ada, tidak ada salinan yang dibuat.
Intinya:
Parameter ke procs yang disimpan tidak pernah mencerminkan eksekusi sebagian dari proc yang disimpan jika mengalami kesalahan.
Bagian 1 dari teka-teki ini adalah bahwa parameter selalu dilewati "oleh nilai". Dan, hanya ketika parameter ditandai sebagai OUTPUT
dan Prosedur Tersimpan berhasil diselesaikan, nilai saat ini benar-benar dikirim kembali. Jika OUTPUT
nilai benar-benar lulus "dengan referensi", maka penunjuk ke lokasi di memori variabel itu akan menjadi hal yang diteruskan, bukan nilai itu sendiri. Dan jika Anda melewati pointer (yaitu alamat memori) maka setiap perubahan yang dibuat segera tercermin, bahkan jika baris berikutnya dari Prosedur Tersimpan menyebabkan kesalahan dan membatalkan eksekusi.
Singkatnya Bagian 1: nilai variabel selalu disalin; mereka tidak dirujuk oleh alamat memori mereka.
Dengan mengingat Bagian 1, kebijakan untuk selalu menyalin nilai variabel dapat menyebabkan masalah sumber daya ketika variabel yang disalurkan cukup besar. Saya belum diuji untuk melihat bagaimana jenis gumpalan ditangani ( VARCHAR(MAX)
, NVARCHAR(MAX)
, VARBINARY(MAX)
, XML
, dan orang-orang yang tidak boleh digunakan lagi: TEXT
, NTEXT
, dan IMAGE
), tetapi aman untuk mengatakan bahwa setiap tabel data yang lewat di bisa menjadi cukup besar. Adalah masuk akal bagi mereka yang mengembangkan fitur TVP untuk menginginkan kemampuan "pass-by-reference" yang sebenarnya untuk mencegah fitur baru mereka yang keren menghancurkan sejumlah sistem yang sehat (yaitu menginginkan pendekatan yang lebih skalabel). Seperti yang Anda lihat dalam dokumentasi itulah yang mereka lakukan:
Transact-SQL mengirimkan parameter bernilai tabel ke rutinitas dengan referensi untuk menghindari penyalinan data input.
Juga, masalah manajemen memori ini bukan konsep baru karena dapat ditemukan di SQLCLR API yang diperkenalkan dalam SQL Server 2005 (TVP diperkenalkan dalam SQL Server 2008). Ketika meneruskan NVARCHAR
dan VARBINARY
data ke dalam kode SQLCLR (yaitu parameter input pada metode .NET dalam Majelis SQLCLR), Anda memiliki opsi untuk pergi dengan pendekatan "menurut nilai" dengan menggunakan salah satu SqlString
atau SqlBinary
masing - masing, atau Anda dapat pergi dengan "dengan referensi "pendekatan dengan menggunakan salah satu SqlChars
atau SqlBytes
masing - masing. Tipe SqlChars
dan SqlBytes
memungkinkan streaming penuh data ke .NET CLR sehingga Anda dapat menarik potongan kecil dari nilai besar sebagai lawan menyalin seluruh nilai 200 MB (hingga 2 GB, kanan).
Singkatnya Bagian 2: TVP, pada dasarnya, akan memiliki kecenderungan untuk mengkonsumsi banyak memori (dan karenanya menurunkan kinerja) jika tetap dalam model "selalu salin nilai". Oleh karena itu TVP melakukan "pass by reference" yang sebenarnya.
Bagian terakhir adalah mengapa Bagian 2 penting: mengapa melewati TVP benar-benar "dengan referensi" alih-alih membuat salinannya mengubah apa pun. Dan itu dijawab oleh tujuan desain yang merupakan dasar untuk Bagian 1: Prosedur Tersimpan yang tidak selesai dengan sukses seharusnya tidak mengubah, dengan cara apa pun, parameter input apa pun, apakah ditandai OUTPUT
atau tidak. Mengizinkan operasi DML akan memiliki pengaruh langsung pada nilai TVP karena ada dalam konteks panggilan (karena dengan referensi berarti Anda mengubah hal yang diteruskan, bukan salinan dari apa yang diteruskan).
Sekarang, seseorang, di suatu tempat, pada titik ini mungkin berbicara dengan monitor mereka dan berkata, "Yah, bangun saja di fasilitas automagic untuk mengembalikan semua perubahan yang dilakukan pada parameter TVP jika ada yang dimasukkan ke dalam Prosedur Tersimpan. Duh. Masalah terpecahkan." Tidak secepat itu. Di sinilah sifat Variabel Tabel masuk: perubahan yang dibuat pada Variabel Tabel tidak terikat oleh Transaksi! Jadi tidak ada cara untuk mengembalikan perubahan. Dan pada kenyataannya, ini adalah trik yang digunakan untuk menyimpan info yang dihasilkan dalam suatu transaksi jika perlu ada rollback :-).
Untuk meringkas Bagian 3: Tabel-Variabel tidak memungkinkan untuk "membatalkan" perubahan yang dibuat untuk mereka dalam kasus kesalahan yang menyebabkan Prosedur Tersimpan dibatalkan. Dan ini melanggar tujuan desain memiliki parameter tidak pernah mencerminkan eksekusi parsial (Bagian 1).
Ergo: yang READONLY
kata kunci diperlukan untuk mencegah operasi DML pada TVPs karena mereka adalah Variabel Tabel yang benar-benar melewati "dengan referensi", dan karenanya setiap modifikasi mereka akan segera tercermin, bahkan jika disimpan prosedur menemukan kesalahan, dan tidak ada cara lain untuk mencegah itu.
Selain itu, parameter dari tipe data lain tidak dapat digunakan READONLY
karena mereka sudah menyalin apa yang diteruskan, sehingga tidak akan melindungi apa pun yang belum dilindungi. Itu, dan cara parameter dari tipe data lain berfungsi dimaksudkan sebagai baca-tulis, jadi mungkin akan lebih banyak pekerjaan untuk mengubah API yang sekarang menyertakan konsep hanya-baca.
TYPE
variabel TVP pengguna atau aDECLARE x as TABLE (...)
) dengan prosedur tersimpan? Bisakah saya melakukannya, meskipun dengan jejak memori yang lebih besar, dengan fungsi alih-alih denganset @tvp = myfunction(@tvp)
jikaRETURNS
nilai fungsi saya adalah tabel dengan DDL yang sama dengan tipe TVP?SET
variabel tabel, setidaknya tidak saya sadari. Dan bahkan jika Anda bisa: a) Anda tidak dapat mengakses set hasil melalui=
operator, dan b) TVP masih ditandai sebagaiREADONLY
, jadi pengaturan itu akan melanggar itu. Hanya membuang konten ke tabel temp atau variabel tabel lain yang Anda buat di dalam proc.Jawaban komunitas Wiki dihasilkan dari komentar atas pertanyaan oleh Martin Smith
Ada item Connect aktif (dikirimkan oleh Erland Sommarskog) untuk ini:
Santai pembatasan bahwa parameter tabel harus dibaca hanya ketika SPs saling memanggil
Satu-satunya tanggapan oleh Microsoft sejauh ini mengatakan (penekanan ditambahkan):
sumber