เหตุใด SQL Server จึงส่งคืนแถวบางแถวขณะที่ยังดำเนินการค้นหาและบางครั้งไม่


33

มีข้อความค้นหาที่เมื่อเรากด "execute" มันจะแสดงบางแถวและมันจะเพิ่มขึ้นเรื่อย ๆ แต่แบบสอบถามนั้นยังไม่จบ แต่บางครั้งมันจะรอจนกว่าจะสิ้นสุดการสืบค้น

ทำไมสิ่งนี้ถึงเกิดขึ้น มีวิธีควบคุมสิ่งนี้หรือไม่?

คำตอบ:


43

คำตอบตามปกติ (ไม่เป็นไรเวลาส่วนใหญ่) อยู่ในแผนการดำเนินการ

มีโอเปอเรเตอร์บางตัวที่ต้องการให้ทุกแถวมาถึงพวกมันก่อนที่พวกเขาจะสามารถเริ่มการประมวลผลแถวเหล่านั้นและส่งพวกมันแบบดาวน์สตรีมเช่น:

  • Hash เข้าร่วม (ในการสร้างตารางแฮช)
  • Hash Match
  • จัดเรียง (ยกเว้น Hash Flow Distinct)

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

มีโอเปอเรเตอร์อื่น ๆ ที่สามารถเริ่มการสตรีมหรือส่งผ่านแถวที่พบได้ทันที

  • ลูปซ้อน
  • ดัชนีสนับสนุนการผสานการเข้าร่วม
  • มวลรวม

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

สิ่งนี้อาจเกิดขึ้นได้เพราะเป้าหมายของแถวแนะนำโดยคุณหรือจากเครื่องมือเพิ่มประสิทธิภาพ

นอกจากนี้ยังสามารถเกิดขึ้นได้หากเลือกแผนไม่ดีด้วยเหตุผลบางอย่าง (การขาดความสามารถในการ SARGability, การดมพารามิเตอร์, สถิติไม่เพียงพอ ฯลฯ ) แต่ก็ต้องใช้การขุดมากขึ้น

สำหรับข้อมูลเพิ่มเติมโปรดดูบล็อกของ Rob Farley ที่นี่

และซีรีส์พอลไวท์ในเป้าหมายแถวที่นี่ , ที่นี่ , ที่นี่และที่นี่

ควรสังเกตว่าถ้าคุณกำลังพูดถึง SSMS แถวจะปรากฏเฉพาะเมื่อบัฟเฟอร์เต็มแล้วเท่านั้นไม่ใช่เพียงความจำใจเท่านั้น


14

ถ้าฉันเข้าใจสิ่งที่คุณสังเกตนี่คือวิธีที่ Studio จัดการแสดงผลแถวและมีส่วนเกี่ยวข้องกับวิธีที่ SQL Server ส่งคืนแถว ในความเป็นจริงบ่อยครั้งเมื่อคุณส่งคืนผลลัพธ์ที่มีขนาดใหญ่ไปยัง SSMS และพยายามแสดงผลในตาราง SSMS ไม่สามารถติดตามได้และ SQL Server จะรอให้แอปประมวลผลแถวมากขึ้น ในกรณีนี้คุณจะเห็นการASYNC_NETWORK_IOรอคอยของSQL Server

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

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

RAISERROR('', 0, 1) WITH NOWAIT;

แต่สิ่งนี้จะไม่ช่วยเมื่อคุณพยายามทำให้ SSMS แสดงแถวได้เร็วขึ้นเมื่อเอาต์พุตทั้งหมดมาจากคำสั่งเดียว

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

มีแฮ็กบางอย่างOPTION (FAST 100)ที่จะปรับให้เหมาะสำหรับการเรียก 100 แถวแรกเหล่านั้น (หรือ 100 แถวใด ๆ หากไม่มีด้านนอกORDER BY) แต่สิ่งนี้อาจทำให้เกิดการเรียกคืนได้ช้ากว่าสำหรับส่วนที่เหลือของแถวและแผนที่มีมากกว่า โดยรวมแล้วไม่มีประสิทธิภาพดังนั้นจึงไม่ใช่ตัวเลือกที่ใช้ในการไปที่ IMHO


1

คำถามของคุณไม่เกี่ยวกับ SQLServer ต่อ se แต่:

  • SQLServer
  • เครือข่าย
  • SSMS เป็นแอปพลิเคชันไคลเอนต์

มีวิธีควบคุมสิ่งนี้หรือไม่?

คำตอบสั้น ๆ :

  1. ลองsqlcmdแทนssmsหรือsqlcmd- โหมดของssms
  2. ตรวจสอบการเชื่อมต่อและการตั้งค่าเซสชันของคุณ

คำตอบยาว :

แน่นอน! แต่ไม่ได้หนึ่ง - prob

  1. ดำเนินการแบบสอบถามของคุณด้วยsqlcmdหรือในsqlcmdโหมดโหมดใน ssms
  2. หากคุณต้องการยกเว้นบทบาทของเครือข่าย - เรียกใช้แบบสอบถามของคุณบนเซิร์ฟเวอร์ด้วยการเชื่อมต่อหน่วยความจำที่แชร์
  3. หากประสิทธิภาพการสืบค้นไม่น่าพอใจแม้จะใช้การเชื่อมต่อหน่วยความจำร่วม - วิเคราะห์แผนการดำเนินการของคุณ หากข้อความค้นหาทำงานไม่ดีผ่านเครือข่ายให้ติดต่อผู้ดูแลเครือข่ายเครือข่ายของคุณเพื่อขอความช่วยเหลือ หากการค้นหาของคุณทำงานได้ไม่ดีใน SSMS เท่านั้น - อ่านเพิ่มเติม
  4. ตอนนี้เรามั่นใจว่าปัญหาอยู่ที่ฝั่งไคลเอ็นต์ (ssms ในกรณีนี้) ดูการเชื่อมต่อและการตั้งค่าเซสชันใน SSMS อย่าเชื่ออินเทอร์เฟซ ssmsและตรวจสอบกับ SQL Profiler: ค้นหาการเชื่อมต่อของคุณspidและคุณจะได้รับรายการการตั้งค่าเซสชันทั้งหมด เปรียบเทียบกับการตั้งค่าของsqlcmdเซสชัน หากไม่มีสิ่งใดคลิก - คัดลอกการตั้งค่าเซสชันทั้งหมดจากผู้สร้างโปรไฟล์ลงในสคริปต์การสืบค้นของคุณให้ดำเนินการในsqlcmdโหมดและค่อยๆเปลี่ยนการตั้งค่าคุณจะพบผู้กระทำผิด

โชคดี!


-2

ในการเพิ่มคำตอบของ sp_BlitzErik ให้นำตัวอย่างโดยใช้ a NOT IN ()พร้อมกับตัวเลือกย่อย เพื่อที่จะตรวจสอบว่ารายการนั้นเป็นผลของการสืบค้นซ้อนหรือไม่มันเป็น (โดยทั่วไป) ที่จำเป็นในการดึงผลลัพธ์ทั้งหมด

ดังนั้นวิธีที่ง่ายวิธีหนึ่งที่ฉันได้พบในการปรับปรุงประสิทธิภาพการทำงานของคำสั่งดังกล่าวคือการเขียนให้พวกเขาเป็นผู้LEFT OUTER JOINที่มีที่เงื่อนไขRIGHTด้านเป็นโมฆะ (คุณสามารถพลิกมันไปรอบ ๆ แน่นอน แต่ที่ใช้RIGHT OUTER JOINS?) สิ่งนี้จะช่วยให้ผลลัพธ์เริ่มกลับมาทันที


ฉันไม่คิดอย่างนั้น หากคอลัมน์ที่เปรียบเทียบไม่เป็นโมฆะผลลัพธ์ควรเหมือนกันและเป็นแผน - โดยปกติ - เหมือนกันสำหรับ antijoin ทั้ง 3 เวอร์ชัน (ไม่ได้อยู่ในไม่อยู่ไม่มีซ้ายเข้าร่วม / เป็นโมฆะ) ไม่จำเป็นต้องดึงผลลัพธ์ทั้งหมด
ypercubeᵀᴹ

หากการเลือกย่อยเป็นสิ่งที่ซับซ้อนจริง ๆ ที่แบบสอบถามที่สร้างขึ้นจะต้องประเมินการเลือกย่อยทั้งหมดก่อนที่จะตรวจสอบเงื่อนไขไม่อยู่ในWHERE t.x IN (<complex SELECT subquery>), LEFT JOIN ที่เทียบเท่ากันLEFT JOIN (<complex SELECT subquery>) AS r ON r.x = t.x .... WHERE r.x IS NULLแล้วแบบสอบถามย่อยจะได้รับการประเมินเช่นกัน (เช่นแผนซับซ้อนที่ไม่มี ในเวอร์ชัน)
ypercubeᵀᴹ

@ ypercubeᵀᴹมันได้ผลสำหรับฉันในอดีต ฉันเคยเห็นข้อความค้นหาเปลี่ยนจากนาทีเพื่อกลับไปยังหน้าย่อย
JimmyJames

@ ypercubeᵀᴹฉันรวบรวมตัวอย่างง่ายๆใน Oracle (ขออภัยฉันไม่สามารถเข้าถึง SQLServer ได้ในขณะนี้) และพวกเขาก็มีแผนการอธิบายที่แตกต่างกันอย่างแน่นอน บางทีพวกเขาอาจไม่มีความแตกต่างที่มีความหมาย แต่พวกเขาก็ดูแตกต่างกันไป
JimmyJames

@JimmyJames: ไม่ใช่วิธีที่ง่ายถ้าคุณต้องการประสิทธิภาพที่มั่นคงและ "การเพิ่มประสิทธิภาพ" เช่นนั้นมีความอ่อนไหวมากสำหรับรุ่น SQLServer และอย่าพลาดข้อผิดพลาดกับ Oracle (เวอร์ชันใด) ในอดีต SQLServer ต้องการNOT EXISTSแต่ Oracle NOT INในแบบสอบถาม แต่วันนี้ต้องถือว่าเป็นข้อผิดพลาดในตัวสร้างแผน
Alex Yu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.