คำอธิบายดูเหมือนจะเชื่อมโยงกับการรวมกันของ: ก) รายละเอียดจากบล็อกที่เชื่อมโยงที่ไม่ได้กล่าวถึงในคำถามนี้ข) วิธีปฏิบัติของ TVPs ที่เหมาะสมในวิธีการที่พารามิเตอร์ได้รับการเข้าและออก, c) และธรรมชาติ ของตัวแปรตาราง
รายละเอียดที่ขาดหายไปในโพสต์บล็อกที่เชื่อมโยงนั้นเป็นวิธีการที่ตัวแปรถูกส่งผ่านเข้าและออกจากกระบวนงานและฟังก์ชันที่เก็บไว้ (ซึ่งเกี่ยวข้องกับการใช้ถ้อยคำในคำถามของ "รุ่นที่ปลอดภัยกว่าของรหัสผ่านโดยอ้างอิงหากพารามิเตอร์ OUTPUT") :
TSQL ใช้ซีแมนทิกส์การคัดลอก / คัดลอกเพื่อส่งพารามิเตอร์ไปยังโพรซีเดอร์และฟังก์ชันที่เก็บไว้ ....
... เมื่อ proc ที่จัดเก็บเสร็จสิ้นการดำเนินการ (โดยไม่ต้องกดปุ่มผิดพลาด) การคัดลอกออกจะทำการปรับปรุงพารามิเตอร์ที่ส่งผ่านด้วยการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นใน proc ที่เก็บไว้
ประโยชน์ที่แท้จริงของวิธีนี้คือในกรณีที่เกิดข้อผิดพลาด หากมีข้อผิดพลาดเกิดขึ้นในระหว่างการดำเนินการของโพรซีเดอร์ที่เก็บไว้การเปลี่ยนแปลงใด ๆ ที่ทำกับพารามิเตอร์จะไม่แพร่กระจายกลับไปยังผู้โทร
หากคีย์เวิร์ด OUTPUT ไม่มีอยู่จะไม่มีการคัดลอกออก
บรรทัดล่าง:
พารามิเตอร์ของ procs ที่เก็บไว้จะไม่สะท้อนการเรียกใช้ proc ที่จัดเก็บบางส่วนหากพบข้อผิดพลาด
ส่วนที่ 1 ของปริศนานี้คือว่าพารามิเตอร์ที่มักจะผ่านไป "โดยค่า" และมันก็ต่อเมื่อพารามิเตอร์ถูกทำเครื่องหมายเป็นOUTPUT
และขั้นตอนการจัดเก็บเสร็จสมบูรณ์แล้วว่าค่าปัจจุบันจะถูกส่งกลับจริง หากOUTPUT
ค่าถูกส่งผ่านอย่างแท้จริง "โดยการอ้างอิง" ตัวชี้ไปยังตำแหน่งในหน่วยความจำของตัวแปรนั้นจะเป็นสิ่งที่ผ่านไปไม่ใช่ค่าตัวเอง และถ้าคุณผ่านตัวชี้ (เช่นที่อยู่หน่วยความจำ) การเปลี่ยนแปลงใด ๆ ที่ทำจะถูกสะท้อนทันทีแม้ว่าบรรทัดถัดไปของกระบวนงานที่เก็บไว้จะทำให้เกิดข้อผิดพลาดและจะยกเลิกการทำงาน
เพื่อสรุปส่วนที่ 1: ค่าตัวแปรจะถูกคัดลอกเสมอ พวกเขาไม่ได้อ้างอิงโดยที่อยู่หน่วยความจำ
เมื่อคำนึงถึงส่วนที่ 1 นโยบายการคัดลอกค่าตัวแปรสามารถนำไปสู่ปัญหาทรัพยากรเมื่อตัวแปรที่ส่งผ่านมีขนาดใหญ่มาก ฉันไม่ได้ทดสอบเพื่อดูว่าชนิดหยดได้รับการจัดการ ( VARCHAR(MAX)
, NVARCHAR(MAX)
, VARBINARY(MAX)
, XML
และผู้ที่ไม่ควรนำมาใช้อีกต่อไป: TEXT
, NTEXT
และIMAGE
) แต่มันมีความปลอดภัยที่จะบอกว่าตารางของข้อมูลใด ๆ ที่ถูกส่งผ่านไปในอาจจะมีขนาดค่อนข้างใหญ่ มันจะสมเหตุสมผลสำหรับผู้ที่พัฒนาฟีเจอร์ TVP เพื่อต้องการความสามารถในการ "ส่งต่ออ้างอิง" อย่างแท้จริงเพื่อป้องกันไม่ให้ฟีเจอร์ใหม่ ๆ ของพวกเขาทำลายระบบที่มีสุขภาพดีจำนวนมาก (เช่นต้องการวิธีที่ปรับขนาดได้มากกว่า) อย่างที่คุณเห็นในเอกสารนั่นคือสิ่งที่พวกเขาทำ:
Transact-SQL ส่งผ่านพารามิเตอร์ที่มีค่าของตารางไปยังรูทีนโดยอ้างอิงเพื่อหลีกเลี่ยงการทำสำเนาข้อมูลอินพุต
นอกจากนี้ปัญหาการจัดการหน่วยความจำนี้ไม่ได้เป็นแนวคิดใหม่เนื่องจากสามารถพบได้ใน SQLCLR API ที่เปิดตัวใน SQL Server 2005 (TVPs ถูกนำมาใช้ใน SQL Server 2008) เมื่อผ่านNVARCHAR
และVARBINARY
ข้อมูลลงในรหัส SQLCLR (เช่นพารามิเตอร์อินพุตใน. NET method ภายใน SQLCLR Assembly) คุณมีตัวเลือกที่จะไปกับวิธี "โดยค่า" โดยใช้วิธีใดวิธีหนึ่งSqlString
หรือSqlBinary
ตามลำดับหรือคุณสามารถไปกับ "โดยอ้างอิง "วิธีโดยใช้อย่างใดอย่างหนึ่งSqlChars
หรือSqlBytes
ตามลำดับ SqlChars
และSqlBytes
ประเภทอนุญาตให้มีการสตรีมมิ่งเต็มรูปแบบของข้อมูลลงในเครื่อง NET CLR เช่นที่คุณสามารถดึงชิ้นเล็ก ๆ ของค่าที่มีขนาดใหญ่เมื่อเทียบกับการคัดลอกทั้ง 200 เมกะไบต์ (ได้ถึง 2 GB ขวา) มูลค่า
เพื่อสรุปส่วนที่ 2: TVPs โดยธรรมชาติของพวกเขาจะมีแนวโน้มที่จะใช้หน่วยความจำจำนวนมาก (และทำให้ประสิทธิภาพลดลง) หากอยู่ในรุ่น ดังนั้น TVPs จึงทำ "pass by reference" อย่างแท้จริง
ชิ้นสุดท้ายคือเหตุผลที่ตอนที่ 2 สำคัญ: ทำไมต้องผ่าน TVP อย่างแท้จริง "โดยการอ้างอิง" แทนที่จะทำสำเนามันเปลี่ยนแปลงอะไร และนั่นคือคำตอบโดยเป้าหมายการออกแบบที่เป็นพื้นฐานสำหรับส่วนที่ 1: ขั้นตอนการจัดเก็บที่ไม่เสร็จสมบูรณ์ไม่ควรเปลี่ยนแปลงพารามิเตอร์อินพุตใด ๆ ไม่ว่าด้วยวิธีใดก็ตามไม่ว่าจะทำเครื่องหมายไว้OUTPUT
หรือไม่ก็ตาม การอนุญาตการดำเนินการ DML จะส่งผลกระทบทันทีต่อมูลค่าของ TVP เนื่องจากมีอยู่ในบริบทการโทร (เนื่องจากผ่านการอ้างอิงหมายความว่าคุณกำลังเปลี่ยนสิ่งที่ส่งผ่านไม่ใช่สำเนาของสิ่งที่ส่งผ่าน)
ตอนนี้มีใครบางคนอยู่ ณ จุดนี้อาจพูดคุยกับจอมอนิเตอร์ของพวกเขาพูดว่า "ดีเพียงแค่สร้างในสถานที่อัตโนมัติเพื่อย้อนกลับการเปลี่ยนแปลงใด ๆ ที่ทำกับพารามิเตอร์ TVP ถ้ามีการส่งผ่านไปยังขั้นตอนการจัดเก็บ Duh. ไม่เร็วนัก นี่คือที่มาของลักษณะของตัวแปรตาราง: การเปลี่ยนแปลงที่ทำกับตัวแปรตารางจะไม่ถูกผูกมัดโดยธุรกรรม! ดังนั้นจึงไม่มีวิธีที่จะย้อนการเปลี่ยนแปลงกลับมา และอันที่จริงนี่เป็นเคล็ดลับที่ใช้ในการบันทึกข้อมูลที่สร้างขึ้นภายในการทำธุรกรรมหากจำเป็นต้องมีการย้อนกลับ :-)
ในการสรุปส่วนที่ 3: ตัวแปรตารางไม่อนุญาตให้มีการเปลี่ยนแปลง "เลิกทำ" ในกรณีที่เกิดข้อผิดพลาดที่ทำให้ขั้นตอนการจัดเก็บยกเลิก และสิ่งนี้เป็นการละเมิดเป้าหมายการออกแบบของการมีพารามิเตอร์ที่ไม่สะท้อนการดำเนินการบางส่วน (ตอนที่ 1)
Ergo:READONLY
จำเป็นต้องใช้คำหลักเพื่อป้องกันการดำเนินงาน DML ใน TVPs เนื่องจากเป็นตัวแปรตารางที่ส่งผ่าน "การอ้างอิง" จริง ๆ และด้วยเหตุนี้การปรับเปลี่ยนใด ๆ ของพวกเขาจะถูกสะท้อนทันทีแม้ว่าขั้นตอนการจัดเก็บพบข้อผิดพลาดและไม่มี วิธีอื่นในการป้องกัน
นอกจากนี้พารามิเตอร์ของประเภทข้อมูลอื่น ๆ ไม่สามารถใช้งานได้READONLY
เนื่องจากเป็นสำเนาของสิ่งที่ส่งผ่านไปแล้วและจะไม่ป้องกันสิ่งที่ไม่ได้รับการป้องกันอยู่แล้ว และวิธีการที่พารามิเตอร์ของงานประเภทข้อมูลอื่นนั้นมีวัตถุประสงค์เพื่ออ่าน - เขียนดังนั้นจึงอาจเป็นงานที่มากยิ่งขึ้นในการแก้ไข API นั้นให้รวมแนวคิดแบบอ่านอย่างเดียว
TYPE
ตัวแปรTVP ผู้ใช้หรือ aDECLARE x as TABLE (...)
) ด้วยโพรซีเดอร์ที่เก็บไว้? ฉันสามารถทำได้แม้ว่าจะมี footprint หน่วยความจำมากขึ้นด้วยฟังก์ชั่นแทนด้วยset @tvp = myfunction(@tvp)
ถ้าRETURNS
ค่าของฟังก์ชั่นของฉันคือตารางที่มี DDL เช่นเดียวกับประเภท TVP?