Mengapa TVP harus READONLY, dan mengapa parameter tipe lainnya tidak READONLY

19

Menurut blog ini, parameter untuk suatu fungsi atau prosedur yang tersimpan pada dasarnya adalah nilai per nilai jika mereka bukan OUTPUTparameter, dan pada dasarnya diperlakukan sebagai versi pass-by-reference yang lebih aman jika itu adalah OUTPUTparameter.

Pada awalnya saya pikir tujuan memaksa TVP untuk dideklarasikan READONLYadalah dengan jelas memberi sinyal kepada pengembang bahwa TVP tidak dapat digunakan sebagai OUTPUTparameter, tetapi harus ada lebih banyak hal yang terjadi karena kita tidak dapat mendeklarasikan non-TVP sebagai READONLY. Misalnya yang berikut gagal:

create procedure [dbo].[test]
@a int readonly
as
    select @a

Msg 346, Level 15,
Status 1, Uji prosedur Parameter "@a" tidak dapat dideklarasikan secara SIAP karena ini bukan parameter bernilai tabel.

  1. Karena statistik tidak disimpan di TVP, apa alasan di balik mencegah operasi DML?
  2. Apakah ini terkait dengan tidak ingin TVP menjadi OUTPUTparameter untuk beberapa alasan?
Erik
sumber

Jawaban:

19

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.

  1. 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 OUTPUTnilai 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.

  2. 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 NVARCHARdan VARBINARYdata 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 SqlStringatau SqlBinarymasing - masing, atau Anda dapat pergi dengan "dengan referensi "pendekatan dengan menggunakan salah satu SqlCharsatau SqlBytesmasing - masing. Tipe SqlCharsdan SqlBytesmemungkinkan 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.

  3. 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 OUTPUTatau 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 READONLYkata 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 READONLYkarena 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.

Solomon Rutzky
sumber
Penjelasan yang sangat rinci. Terima kasih. Jadi tidak ada cara untuk memodifikasi variabel tabel yang lewat (baik TYPEvariabel TVP pengguna atau a DECLARE x as TABLE (...)) dengan prosedur tersimpan? Bisakah saya melakukannya, meskipun dengan jejak memori yang lebih besar, dengan fungsi alih-alih dengan set @tvp = myfunction(@tvp)jika RETURNSnilai fungsi saya adalah tabel dengan DDL yang sama dengan tipe TVP?
mpag
@mpag Terima kasih. TVP adalah variabel tabel, tidak ada perbedaan. Anda tidak meneruskan dalam jenis, Anda meneruskan dalam variabel tabel yang dibuat dari jenis atau dari deklarasi skema eksplisit. Juga, Anda tidak bisa SETvariabel tabel, setidaknya tidak saya sadari. Dan bahkan jika Anda bisa: a) Anda tidak dapat mengakses set hasil melalui =operator, dan b) TVP masih ditandai sebagai READONLY, jadi pengaturan itu akan melanggar itu. Hanya membuang konten ke tabel temp atau variabel tabel lain yang Anda buat di dalam proc.
Solomon Rutzky
Terima kasih lagi. Saya telah memutuskan untuk dasarnya menggunakan pendekatan tabel temp.
mpag
5

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):

Terima kasih atas umpan baliknya. Kami telah menerima umpan balik yang serupa dari sejumlah besar pelanggan. Membiarkan parameter tabel bernilai untuk dibaca / ditulis melibatkan sedikit pekerjaan di sisi SQL Engine serta protokol klien. Karena kendala waktu / sumber daya serta prioritas lainnya, kami tidak akan dapat mengambil pekerjaan ini sebagai bagian dari rilis SQL Server 2008. Namun, kami telah menyelidiki masalah ini dan memasukkannya ke radar sebagai bagian dari rilis SQL Server berikutnya. Kami menghargai dan menyambut umpan balik di sini.

Srini Acharya
Manajer Senior Program
SQL Server Relational Engine

Paul White
sumber