เหตุใด TVP ต้องพร้อมใช้งานและทำไมพารามิเตอร์ประเภทอื่นไม่สามารถอ่านได้อย่างเดียว


19

ตามพารามิเตอร์บล็อกนี้สำหรับฟังก์ชั่นหรือขั้นตอนการจัดเก็บเป็นหลักผ่านตามมูลค่าถ้าพวกเขาไม่ได้OUTPUTพารามิเตอร์และถือว่าเป็นรุ่นที่ปลอดภัยกว่าของการอ้างอิงโดยใช้รหัสผ่านถ้าพวกเขาเป็นOUTPUTพารามิเตอร์

ในตอนแรกที่ผมคิดว่าเป้าหมายของการบังคับให้ TVP ที่จะได้รับการประกาศREADONLYเป็นอย่างชัดเจนส่งสัญญาณไปยังนักพัฒนาที่ TVP ไม่สามารถนำมาใช้เป็นOUTPUTพารามิเตอร์ แต่จะต้องมีมากขึ้นที่เกิดขึ้นเพราะเราไม่สามารถประกาศไม่ใช่ TVP READONLYเป็น ตัวอย่างเช่นล้มเหลว:

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

ข่าวสารเกี่ยวกับ 346, ระดับ 15, สถานะ 1, การทดสอบขั้นตอน
ไม่สามารถประกาศพารามิเตอร์ "@a" แบบเรียลไทม์ได้เนื่องจากไม่ใช่พารามิเตอร์ที่มีค่าเป็นตาราง

  1. เนื่องจากสถิติไม่ได้ถูกจัดเก็บใน TVP เหตุผลในการป้องกันการดำเนินงาน DML คืออะไร
  2. มันเกี่ยวข้องกับการไม่ต้องการให้ TVP เป็นOUTPUTพารามิเตอร์ด้วยเหตุผลบางอย่างหรือไม่?

คำตอบ:


19

คำอธิบายดูเหมือนจะเชื่อมโยงกับการรวมกันของ: ก) รายละเอียดจากบล็อกที่เชื่อมโยงที่ไม่ได้กล่าวถึงในคำถามนี้ข) วิธีปฏิบัติของ TVPs ที่เหมาะสมในวิธีการที่พารามิเตอร์ได้รับการเข้าและออก, c) และธรรมชาติ ของตัวแปรตาราง

  1. รายละเอียดที่ขาดหายไปในโพสต์บล็อกที่เชื่อมโยงนั้นเป็นวิธีการที่ตัวแปรถูกส่งผ่านเข้าและออกจากกระบวนงานและฟังก์ชันที่เก็บไว้ (ซึ่งเกี่ยวข้องกับการใช้ถ้อยคำในคำถามของ "รุ่นที่ปลอดภัยกว่าของรหัสผ่านโดยอ้างอิงหากพารามิเตอร์ OUTPUT") :

    TSQL ใช้ซีแมนทิกส์การคัดลอก / คัดลอกเพื่อส่งพารามิเตอร์ไปยังโพรซีเดอร์และฟังก์ชันที่เก็บไว้ ....

    ... เมื่อ proc ที่จัดเก็บเสร็จสิ้นการดำเนินการ (โดยไม่ต้องกดปุ่มผิดพลาด) การคัดลอกออกจะทำการปรับปรุงพารามิเตอร์ที่ส่งผ่านด้วยการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นใน proc ที่เก็บไว้

    ประโยชน์ที่แท้จริงของวิธีนี้คือในกรณีที่เกิดข้อผิดพลาด หากมีข้อผิดพลาดเกิดขึ้นในระหว่างการดำเนินการของโพรซีเดอร์ที่เก็บไว้การเปลี่ยนแปลงใด ๆ ที่ทำกับพารามิเตอร์จะไม่แพร่กระจายกลับไปยังผู้โทร

    หากคีย์เวิร์ด OUTPUT ไม่มีอยู่จะไม่มีการคัดลอกออก

    บรรทัดล่าง:
    พารามิเตอร์ของ procs ที่เก็บไว้จะไม่สะท้อนการเรียกใช้ proc ที่จัดเก็บบางส่วนหากพบข้อผิดพลาด

    ส่วนที่ 1 ของปริศนานี้คือว่าพารามิเตอร์ที่มักจะผ่านไป "โดยค่า" และมันก็ต่อเมื่อพารามิเตอร์ถูกทำเครื่องหมายเป็นOUTPUT และขั้นตอนการจัดเก็บเสร็จสมบูรณ์แล้วว่าค่าปัจจุบันจะถูกส่งกลับจริง หากOUTPUTค่าถูกส่งผ่านอย่างแท้จริง "โดยการอ้างอิง" ตัวชี้ไปยังตำแหน่งในหน่วยความจำของตัวแปรนั้นจะเป็นสิ่งที่ผ่านไปไม่ใช่ค่าตัวเอง และถ้าคุณผ่านตัวชี้ (เช่นที่อยู่หน่วยความจำ) การเปลี่ยนแปลงใด ๆ ที่ทำจะถูกสะท้อนทันทีแม้ว่าบรรทัดถัดไปของกระบวนงานที่เก็บไว้จะทำให้เกิดข้อผิดพลาดและจะยกเลิกการทำงาน

    เพื่อสรุปส่วนที่ 1: ค่าตัวแปรจะถูกคัดลอกเสมอ พวกเขาไม่ได้อ้างอิงโดยที่อยู่หน่วยความจำ

  2. เมื่อคำนึงถึงส่วนที่ 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" อย่างแท้จริง

  3. ชิ้นสุดท้ายคือเหตุผลที่ตอนที่ 2 สำคัญ: ทำไมต้องผ่าน TVP อย่างแท้จริง "โดยการอ้างอิง" แทนที่จะทำสำเนามันเปลี่ยนแปลงอะไร และนั่นคือคำตอบโดยเป้าหมายการออกแบบที่เป็นพื้นฐานสำหรับส่วนที่ 1: ขั้นตอนการจัดเก็บที่ไม่เสร็จสมบูรณ์ไม่ควรเปลี่ยนแปลงพารามิเตอร์อินพุตใด ๆ ไม่ว่าด้วยวิธีใดก็ตามไม่ว่าจะทำเครื่องหมายไว้OUTPUTหรือไม่ก็ตาม การอนุญาตการดำเนินการ DML จะส่งผลกระทบทันทีต่อมูลค่าของ TVP เนื่องจากมีอยู่ในบริบทการโทร (เนื่องจากผ่านการอ้างอิงหมายความว่าคุณกำลังเปลี่ยนสิ่งที่ส่งผ่านไม่ใช่สำเนาของสิ่งที่ส่งผ่าน)

    ตอนนี้มีใครบางคนอยู่ ณ จุดนี้อาจพูดคุยกับจอมอนิเตอร์ของพวกเขาพูดว่า "ดีเพียงแค่สร้างในสถานที่อัตโนมัติเพื่อย้อนกลับการเปลี่ยนแปลงใด ๆ ที่ทำกับพารามิเตอร์ TVP ถ้ามีการส่งผ่านไปยังขั้นตอนการจัดเก็บ Duh. ไม่เร็วนัก นี่คือที่มาของลักษณะของตัวแปรตาราง: การเปลี่ยนแปลงที่ทำกับตัวแปรตารางจะไม่ถูกผูกมัดโดยธุรกรรม! ดังนั้นจึงไม่มีวิธีที่จะย้อนการเปลี่ยนแปลงกลับมา และอันที่จริงนี่เป็นเคล็ดลับที่ใช้ในการบันทึกข้อมูลที่สร้างขึ้นภายในการทำธุรกรรมหากจำเป็นต้องมีการย้อนกลับ :-)

    ในการสรุปส่วนที่ 3: ตัวแปรตารางไม่อนุญาตให้มีการเปลี่ยนแปลง "เลิกทำ" ในกรณีที่เกิดข้อผิดพลาดที่ทำให้ขั้นตอนการจัดเก็บยกเลิก และสิ่งนี้เป็นการละเมิดเป้าหมายการออกแบบของการมีพารามิเตอร์ที่ไม่สะท้อนการดำเนินการบางส่วน (ตอนที่ 1)

Ergo:READONLYจำเป็นต้องใช้คำหลักเพื่อป้องกันการดำเนินงาน DML ใน TVPs เนื่องจากเป็นตัวแปรตารางที่ส่งผ่าน "การอ้างอิง" จริง ๆ และด้วยเหตุนี้การปรับเปลี่ยนใด ๆ ของพวกเขาจะถูกสะท้อนทันทีแม้ว่าขั้นตอนการจัดเก็บพบข้อผิดพลาดและไม่มี วิธีอื่นในการป้องกัน

นอกจากนี้พารามิเตอร์ของประเภทข้อมูลอื่น ๆ ไม่สามารถใช้งานได้READONLYเนื่องจากเป็นสำเนาของสิ่งที่ส่งผ่านไปแล้วและจะไม่ป้องกันสิ่งที่ไม่ได้รับการป้องกันอยู่แล้ว และวิธีการที่พารามิเตอร์ของงานประเภทข้อมูลอื่นนั้นมีวัตถุประสงค์เพื่ออ่าน - เขียนดังนั้นจึงอาจเป็นงานที่มากยิ่งขึ้นในการแก้ไข API นั้นให้รวมแนวคิดแบบอ่านอย่างเดียว


คำอธิบายอย่างละเอียดมาก ขอบคุณ ดังนั้นจึงไม่มีวิธีใดที่จะแก้ไขตัวแปรตารางที่ส่งผ่าน (ทั้งTYPEตัวแปรTVP ผู้ใช้หรือ a DECLARE x as TABLE (...)) ด้วยโพรซีเดอร์ที่เก็บไว้? ฉันสามารถทำได้แม้ว่าจะมี footprint หน่วยความจำมากขึ้นด้วยฟังก์ชั่นแทนด้วยset @tvp = myfunction(@tvp)ถ้าRETURNSค่าของฟังก์ชั่นของฉันคือตารางที่มี DDL เช่นเดียวกับประเภท TVP?
mpag

@mpag ขอบคุณ TVP เป็นตัวแปรตารางไม่มีความแตกต่าง คุณไม่ผ่านประเภทคุณส่งผ่านตัวแปรตารางที่สร้างจากประเภทหรือจากการประกาศ schema ชัดเจน นอกจากนี้คุณไม่สามารถSETตัวแปรตารางอย่างน้อยก็ไม่รู้ว่าฉันรู้ และแม้ว่าคุณจะทำได้: a) คุณไม่สามารถเข้าถึงชุดผลลัพธ์ผ่าน=โอเปอเรเตอร์และ b) TVP ยังคงถูกทำเครื่องหมายว่าเป็นREADONLYดังนั้นการตั้งค่าจะเป็นการละเมิด เพียงถ่ายโอนเนื้อหาไปยังตาราง temp หรือตัวแปรตารางอื่นที่คุณสร้างภายใน proc
โซโลมอน Rutzky

ขอบคุณอีกครั้ง. ฉันตัดสินใจที่จะใช้วิธีการตารางชั่วคราว
mpag

5

คำตอบ Wiki ชุมชนที่สร้างขึ้นจากความคิดเห็นในคำถามโดยMartin Smith

มีรายการเชื่อมต่อที่ใช้งานอยู่ (ส่งโดย Erland Sommarskog) สำหรับสิ่งนี้:

ผ่อนคลายข้อ จำกัด ว่าพารามิเตอร์ของตารางจะต้องอ่านอย่างเดียวเมื่อ SP โทรหากัน

Microsoft ตอบสนองเพียงอย่างเดียวจนถึงขณะนี้ (เน้นเพิ่ม):

ขอบคุณสำหรับความคิดเห็นเกี่ยวกับเรื่องนี้ เราได้รับคำติชมที่คล้ายกันจากลูกค้าจำนวนมาก การอนุญาตให้พารามิเตอร์ที่มีค่าของตารางที่จะอ่าน / เขียนนั้นเกี่ยวข้องกับการทำงานด้าน SQL Engine และโปรโตคอลไคลเอนต์ เนื่องจากข้อ จำกัด ด้านเวลา / ทรัพยากรรวมถึงลำดับความสำคัญอื่น ๆ เราจะไม่สามารถทำงานนี้ได้เนื่องจากเป็นส่วนหนึ่งของการวางจำหน่าย SQL Server 2008 อย่างไรก็ตามเราได้ตรวจสอบปัญหานี้และให้สิ่งนี้อย่างแน่นหนาในเรดาห์ของเราเพื่อจัดการกับส่วนหนึ่งของ SQL Server รุ่นถัดไป เรายินดีและยินดีรับข้อเสนอแนะที่นี่

Srini Acharya
ผู้จัดการโปรแกรม
SQL Server Relational Engine

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.