ความรู้สึกและประโยชน์ของการใช้ SqlCommand.Prepare () คืออะไร?


14

ฉันเจอรหัสนักพัฒนาซอฟต์แวร์ที่ SqlCommand.Prepare () (ดู MSDN)วิธีการใช้อย่างกว้างขวางล่วงหน้าก่อนการดำเนินการของแบบสอบถาม SQL และฉันสงสัยว่าประโยชน์ของสิ่งนี้คืออะไร?

ตัวอย่าง:

command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();

ฉันได้เล่นไปรอบ ๆ เล็กน้อยและติดตาม การดำเนินการของคำสั่งหลังจากเรียกPrepare()วิธีทำให้ SQL Server ดำเนินการคำสั่งต่อไปนี้:

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1

หลังจากนั้นเมื่อพารามิเตอร์ได้รับมันเป็นค่าและSqlCommand.ExecuteNonQuery()ถูกเรียกต่อไปนี้ได้รับการดำเนินการใน Sql-Server:

exec sp_execute 1,@id=20

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

ฉันคิดออก (และบันทึกไว้ในคำถามอื่น ) ที่ SqlCommands ที่ดำเนินการกับ SqlParameters มักจะถูกห่อในsp_executesqlการเรียกขั้นตอน สิ่งนี้ทำให้ Sql Server สามารถจัดเก็บและนำแผนการอิสระมาใช้ใหม่ของค่าพารามิเตอร์ได้

เกี่ยวกับเรื่องนี้ฉันสงสัยว่าprepare()วิธีการเป็นชนิดที่ไร้ประโยชน์หรือล้าสมัยหรือถ้าฉันหายไปบางสิ่งบางอย่างที่นี่?


ฉันคิดเสมอว่ามันมีบางอย่างเกี่ยวกับการป้องกัน SQL Injection ดังนั้นคุณควรเตรียมแบบสอบถามเพื่อยอมรับตัวแปรบางประเภท ด้วยวิธีนี้หากคุณไม่ส่งผ่านตัวแปรประเภทเหล่านี้การสืบค้นจะล้มเหลว
Mark Sinkinson

แม้ว่าในการบอกว่าคำอธิบาย PHP นี้น่าจะใช้ได้กับ. NET php.net/manual/en/pdo.prepared-statements.php
Mark Sinkinson

"ใช่แล้วครับคนขับเตรียมพร้อมที่จะย้ายออก" "คุณกำลังเตรียมตัวอะไรอยู่!! คุณกำลังเตรียมพร้อมอยู่เสมอ!
Jon of All Trades

คำตอบ:


19

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

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

ควรสังเกตว่าตราบใดที่การทดสอบของฉันแสดง (ซึ่งตรงกับสิ่งที่คุณแสดงในคำถาม) การเรียกSqlCommand.Prepare ()ไม่ได้เป็นการดำเนินการ "เตรียมการ" เพียงอย่างเดียว: เรียกsp_prepexecซึ่งเตรียมและเรียกใช้ SQL ; มันไม่ได้เรียกsp_prepareซึ่งแยกวิเคราะห์และรวบรวมเท่านั้น (และไม่ได้ใช้ในค่าพารามิเตอร์ใด ๆ เพียงชื่อและประเภทข้อมูลของพวกเขา) ดังนั้นจึงไม่มีประโยชน์ "pre-compile" ของการโทรSqlCommand.Prepareเนื่องจากจะดำเนินการทันที (สมมติว่าเป้าหมายคือการเลื่อนการทำงาน) อย่างไรก็ตามมีผลประโยชน์เล็ก ๆ น้อย ๆ ที่น่าสนใจที่มีศักยภาพของการเรียกถึงแม้ว่ามันจะไม่เรียกแทน โปรดดูหมายเหตุ**SqlCommand.Prepare()sp_prepexecsp_prepare ) ที่ด้านล่างสำหรับรายละเอียด

การทดสอบของฉันแสดงให้เห็นว่าแม้ว่าSqlCommand.Prepare()จะถูกเรียกใช้ (และโปรดทราบว่าการโทรนี้ไม่ได้ออกคำสั่งใด ๆ บน SQL Server) ExecuteNonQueryหรือExecuteReaderหลังจากนั้นจะถูกดำเนินการอย่างสมบูรณ์และหากเป็น ExecuteReader จะส่งคืนแถว แต่ถึงกระนั้น SQL Server Profiler จะแสดงว่าการSqlCommand.Execute______()โทรครั้งแรกหลังจากการSqlCommand.Prepare()ลงทะเบียนการโทรเป็นเหตุการณ์ "เตรียม SQL" และการ.Execute___()โทรที่ตามมาจะลงทะเบียนเป็นเหตุการณ์ "Exec Prepared SQL"


รหัสทดสอบ

มันได้รับการอัปโหลดไปยัง Pastebin ที่: http://pastebin.com/Yc2Tfvup รหัสจะสร้างแอปคอนโซล. NET / C # ที่มีวัตถุประสงค์เพื่อให้ทำงานในขณะที่การสืบค้นกลับของ SQL Server Profiler นั้นกำลังทำงานอยู่ (หรือเซสชันเพิ่มเติม) มันหยุดหลังจากแต่ละขั้นตอนดังนั้นมันจะชัดเจนว่าข้อความใดมีผลเฉพาะ


UPDATE

SqlCommand.Prepare()พบข้อมูลเพิ่มเติมและเหตุผลที่อาจเกิดขึ้นเล็กน้อยเพื่อหลีกเลี่ยงการโทร สิ่งหนึ่งที่ผมสังเกตเห็นในการทดสอบของเราและสิ่งที่ควรจะตั้งข้อสังเกตสำหรับทุกคนที่ทำงานว่าการทดสอบคอนโซล App: sp_unprepareไม่เคยมีการโทรอย่างชัดเจนทำ ฉันขุดบ้างและพบโพสต์ต่อไปนี้ในฟอรัม MSDN:

SqlCommand - เตรียมหรือไม่เตรียม?

คำตอบที่ได้รับการยอมรับมีข้อมูลมากมาย แต่ประเด็นสำคัญคือ:

  • **สิ่งที่เตรียมไว้จริงช่วยให้คุณประหยัดเป็นหลักเวลาที่ใช้ในการส่งสตริงแบบสอบถามผ่านสาย
  • แบบสอบถามที่เตรียมไว้ไม่มีการรับประกันว่าจะบันทึกแผนแคชและไม่เร็วกว่าแบบสอบถามเฉพาะกิจเมื่อไปถึงเซิร์ฟเวอร์
  • RPC's (CommandType.StoredProcedure) ไม่ได้อะไรจากการเตรียม
  • บนเซิร์ฟเวอร์หมายเลขอ้างอิงที่เตรียมไว้นั้นเป็นดัชนีในแผนที่ในหน่วยความจำที่มี TSQL เพื่อดำเนินการ
  • แผนที่ถูกจัดเก็บแบบต่อการเชื่อมต่อไม่สามารถใช้การเชื่อมต่อข้ามได้
  • การรีเซ็ตถูกส่งเมื่อไคลเอนต์ใช้การเชื่อมต่อจากพูลล้างแผนที่อีกครั้ง

ฉันยังพบโพสต์ต่อไปนี้จากทีม SQLCAT ซึ่งดูเหมือนจะเกี่ยวข้อง แต่ก็อาจเป็นปัญหาเฉพาะกับ ODBC ในขณะที่ SqlClient ล้างข้อมูลได้อย่างถูกต้องเมื่อกำจัด SqlConnection เป็นการยากที่จะพูดเนื่องจากโพสต์ SQLCAT ไม่ได้กล่าวถึงการทดสอบเพิ่มเติมใด ๆ ที่จะช่วยพิสูจน์ได้ว่าเป็นสาเหตุเช่นการล้างพูลการเชื่อมต่อ ฯลฯ

ระวังคำสั่ง SQL ที่เตรียมไว้


สรุปผลการศึกษา

ระบุว่า:

  • ประโยชน์ที่แท้จริงเพียงอย่างเดียวของการโทรก็SqlCommand.Prepare()คือคุณไม่จำเป็นต้องส่งข้อความค้นหาอีกครั้งผ่านเครือข่าย
  • โทรsp_prepareและsp_prepexecเก็บข้อความแบบสอบถามเป็นส่วนหนึ่งของหน่วยความจำของการเชื่อมต่อ (เช่นหน่วยความจำ SQL Server)

ฉันขอแนะนำไม่ให้โทรSqlCommand.Prepare()เพราะประโยชน์ที่เป็นไปได้เพียงอย่างเดียวคือการประหยัดแพ็กเก็ตเครือข่ายในขณะที่ข้อเสียคือการใช้หน่วยความจำเซิร์ฟเวอร์เพิ่มขึ้น ในขณะที่จำนวนหน่วยความจำที่ใช้อาจน้อยมากในกรณีส่วนใหญ่แบนด์วิดท์เครือข่ายไม่ค่อยมีปัญหาเนื่องจากเซิร์ฟเวอร์ DB ส่วนใหญ่เชื่อมต่อโดยตรงกับแอพเซิร์ฟเวอร์มากกว่า 10 เมกะบิตขั้นต่ำ (น่าจะเป็น 100 เมกะบิตหรือ Gigabit ในปัจจุบัน) และบางอันก็อยู่ในกล่องเดียวกัน ;-) และหน่วยความจำนั้นมักเป็นทรัพยากรที่หายากกว่าแบนด์วิดท์เครือข่าย

ฉันคิดว่าสำหรับทุกคนที่เขียนซอฟต์แวร์ที่สามารถเชื่อมต่อกับฐานข้อมูลหลาย ๆ แห่งได้ความสะดวกสบายของอินเทอร์เฟซมาตรฐานอาจเปลี่ยนการวิเคราะห์ต้นทุน / ผลประโยชน์นี้ แต่ในกรณีนี้ฉันต้องเชื่อว่ามันยังง่ายพอที่จะทำให้เป็นนามธรรม "มาตรฐาน" อินเทอร์เฟซฐานข้อมูลที่ช่วยให้ผู้ให้บริการต่าง ๆ (และด้วยเหตุนี้ความแตกต่างระหว่างพวกเขาเช่นไม่จำเป็นต้องโทรPrepare()) การเขียนโปรแกรมที่มุ่งเน้นซึ่งคุณน่าจะทำในรหัสแอปของคุณแล้ว ;-)


ขอบคุณสำหรับคำตอบที่ครอบคลุมนี้ ฉันทดสอบขั้นตอนของคุณและมีความคลาดเคลื่อนสองอย่างจากความคาดหวัง / ผลลัพธ์ของคุณ: ในขั้นตอนที่ 4 ฉันได้รับผลลัพธ์เพิ่มเติม ในขั้นตอนที่ 10 ฉันได้รับ plan_handle มากกว่าในขั้นตอนที่ 2
Magier

1
@Magier ไม่แน่ใจเกี่ยวกับขั้นตอนที่ 4 ... บางทีคุณอาจมีตัวเลือก SET ที่แตกต่างกัน หรืออย่างใดอย่างหนึ่งข้อความค้นหาระหว่างทั้งสองนั้นแตกต่างกันอย่างไร แม้แต่ความแตกต่างของตัวละครตัวเดียว (มองเห็นหรือไม่) ก็เป็นแผนที่แตกต่างกัน คัดลอกและวางจากหน้าเว็บนี้อาจไม่ช่วยดังนั้นคัดลอกแบบสอบถาม (ทุกสิ่งระหว่างเครื่องหมายคำพูดเดี่ยว) จากsp_prepareการเรียกไปsp_executesqlยังเพื่อให้แน่ใจ สำหรับขั้นตอนที่ 10 ใช่ฉันได้มากขึ้นการทดสอบเมื่อวานนี้และได้เห็นโอกาสบางอย่างที่มีด้ามจับที่แตกต่างกันสำหรับแต่ก็ยังไม่เคยเหมือนกันสำหรับsp_prepare sp_executesqlฉันจะทดสอบอีกหนึ่งสิ่งและอัปเดตคำตอบของฉัน
โซโลมอน Rutzky

1
@Magier จริง ๆ แล้วการทดสอบที่ฉันใช้ก่อนหน้านี้ครอบคลุมและฉันไม่คิดว่าจะมีการใช้ซ้ำของแผนโดยเฉพาะอย่างยิ่งในแง่ของการโพสต์ฟอรั่ม MSDN ที่บอกว่าแคชเพียง SQL ฉันยังสงสัยว่าจะสามารถใช้ plan_handle กลับมาใช้ใหม่ได้อย่างไรในขณะที่sp_executesqlทำไม่ได้หรือไม่ แต่ไม่ว่าเวลาแยกวิเคราะห์จะยังคงอยู่sp_prepareถ้าแผนถูกลบออกจากแคชดังนั้นฉันจะลบส่วนหนึ่งของคำตอบของฉัน (น่าสนใจ แต่ไม่เกี่ยวข้อง) ส่วนนั้นเป็นโน้ตด้านที่น่าสนใจมากกว่านี้เพราะมันไม่ได้ตอบคำถามโดยตรงของคุณ ทุกอย่างอื่นยังคงใช้
โซโลมอน Rutzky

1
นี่เป็นคำอธิบายที่ฉันกำลังมองหา
CSharpie

5

เกี่ยวกับเรื่องนี้ฉันสงสัยว่าวิธีการเตรียม () เป็นชนิดที่ไร้ประโยชน์หรือล้าสมัยหรือถ้าฉันขาดอะไรบางอย่างที่นี่?

ฉันจะบอกว่าวิธีการเตรียมความพร้อมมีค่า จำกัด SqlCommand โลกไม่ใช่ว่ามันไร้ประโยชน์ทั้งหมด ข้อดีอย่างหนึ่งคือการเตรียมความพร้อมเป็นส่วนหนึ่งของอินเทอร์เฟซ IDbCommand ที่ SqlCommand ใช้ สิ่งนี้อนุญาตให้โค้ดเดียวกันทำงานกับผู้ให้บริการ DBMS อื่น ๆ (ซึ่งอาจต้องเตรียม) โดยไม่มีตรรกะตามเงื่อนไขเพื่อพิจารณาว่าควรเรียกใช้การเตรียมการหรือไม่

การเตรียมการไม่มีค่ากับผู้ให้บริการ. NET สำหรับ SQL Server นอกเหนือจากกรณีการใช้งานนี้ AFAIK

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