คำอธิบายดูเหมือนจะเชื่อมโยงกับการรวมกันของ: ก) รายละเอียดจากบล็อกที่เชื่อมโยงที่ไม่ได้กล่าวถึงในคำถามนี้ข) วิธีปฏิบัติของ 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?