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