ข้อผิดพลาดของ SQL Server 8632 เนื่องจากมีรายการมากกว่า 100,000 รายการในส่วนคำสั่ง WHERE


16

ปัญหาของฉัน (หรืออย่างน้อยข้อความแสดงข้อผิดพลาด) คล้ายกันมากกับตัวประมวลผลแบบสอบถามที่ไม่มีทรัพยากรภายใน - แบบสอบถาม SQL ที่ยาวมาก

ลูกค้าของฉันกำลังทำงานกับ SQL select-query ซึ่งมี where-clause พร้อมกับ 100,000 รายการ

การค้นหาล้มเหลวด้วยข้อผิดพลาด 8632 และข้อความแสดงข้อผิดพลาด

ข้อผิดพลาดภายใน: ถึงขีด จำกัด บริการนิพจน์แล้ว โปรดค้นหานิพจน์ที่ซับซ้อนที่อาจเกิดขึ้นในข้อความค้นหาของคุณและลองทำให้มันง่ายขึ้น)

ฉันพบว่ามันแปลกมากที่ข้อความแสดงข้อผิดพลาดนี้ถูกส่งไปที่ 100,000 รายการดังนั้นฉันสงสัยว่านี่เป็นค่าที่กำหนดได้หรือไม่ นี่คือกรณีและในกรณีที่ใช่ฉันจะเพิ่มมูลค่านี้เป็นค่าที่สูงขึ้นได้อย่างไร

ในMSDNมีข้อเสนอให้เขียนแบบสอบถามซ้ำ แต่ฉันต้องการหลีกเลี่ยงปัญหานี้

ในขณะเดียวกันฉันพบว่ารายการของสิ่งที่ฉันพูดถึงมีจำนวนธรรมชาติ แต่บางรายการก็เรียงตามลำดับ (เช่น (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20)

สิ่งนี้ทำให้ SQL อยู่ตรงไหน - clause:

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

ฉันสามารถแปลงให้เป็น:

where (entry between 1 and 3) OR
      (entry between 6 and 10) OR
      (entry between 12 and 13) OR
      (entry between 15 and 20)

สามารถสั้นลงโดย:

where entry in (1,...,3,6,...,10,12,13,15,...,20)

... หรืออะไรทำนองนี้? (ฉันรู้ว่ามันใช้เวลานาน แต่จะทำให้การอัปเดตซอฟต์แวร์ง่ายขึ้นและอ่านง่ายขึ้น)

สำหรับข้อมูลของคุณ: ข้อมูลใน where-clause เป็นผลลัพธ์ของการคำนวณทำบนตารางอื่น: อันดับแรกรายการของตารางนั้นจะถูกอ่านและกรองที่จุดเริ่มต้นจากนั้นการประมวลผลพิเศษบางอย่างเกิดขึ้น (ซึ่งเป็นไปไม่ได้ที่จะใช้ SQL) ผลลัพธ์ของการประมวลผลพิเศษนั้นคือการกรองเพิ่มเติมและผลลัพธ์ของการประมวลผลที่ใช้ใน where-clause เนื่องจากเป็นไปไม่ได้ที่จะเขียนตัวกรองที่สมบูรณ์ใน SQL จึงใช้วิธีดังกล่าว เห็นได้ชัดว่าเนื้อหาของคำสั่งย่อยอาจเปลี่ยนแปลงได้ในทุกการประมวลผลดังนั้นความต้องการโซลูชันแบบไดนามิก


7
ในการตอบกลับการแก้ไขของคุณ: WHERE INไม่ไม่รองรับไวยากรณ์ของช่วงนั้น นอกจากนี้ควรจะWHERE () OR () OR ()ไม่และ WHERE IN (SELECT myID FROM #biglist)แต่การที่จะใช้ข้อเสนอแนะของเบรนต์ของคุณไม่จริงต้องเปลี่ยนแบบสอบถามทั้งหมดคุณก็สามารถทำได้ และ#biglistอาจเป็นตารางจริง (ถาวร) หรือตารางชั่วคราวที่คุณทำได้ทันที
BradC

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

คำตอบ:


67

หากต้องการค้นหามากกว่า 100,000 ค่าให้วางไว้ในตารางชั่วคราวแทนหนึ่งแถวต่อมูลค่าที่คุณกำลังค้นหา จากนั้นเข้าร่วมคิวรีของคุณไปยังตารางชั่วคราวสำหรับการกรอง

บางสิ่งที่มีมากกว่า 100,000 ค่าไม่ใช่พารามิเตอร์ - เป็นตาราง แทนที่จะคิดถึงการเพิ่มขีด จำกัด ให้พิจารณากฎสิบเปอร์เซ็นต์ของ Swart : หากคุณกำลังเข้าใกล้ขีด จำกัด 10% ของ SQL Server คุณอาจจะมีเวลาไม่ดี


1
ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Paul White Reinstate Monica

10

หากคุณกำลังจะเปลี่ยนแอปให้พิจารณาด้วย

(ก) การใช้ TVP สำหรับทั้งชุดของค่า - คุณจะต้องสร้างDataTableใน C # และผ่านมันเป็นขั้นตอนการเก็บใช้StructuredTypeเป็นผมแสดงให้เห็นถึงที่นี่ (หวังว่า 100,000 รายการไม่ปกติเนื่องจากความสามารถในการปรับขยายอาจเป็นปัญหาไม่ว่าคุณจะใช้วิธีใด)

CREATE TYPE dbo.optionA AS TABLE(value int PRIMARY KEY);
GO

CREATE PROCEDURE dbo.procedure_optionA
  @t dbo.optionA READONLY
AS
BEGIN
  SET NOCOUNT ON;
  SELECT <cols> FROM dbo.<table> AS t
    INNER JOIN @t AS tvp
    ON t.entry = tvp.value;
END
GO

หรือ

(b) ใช้ TVP เพื่อส่งผ่านขอบเขตบนและล่างของช่วงและเขียนการเข้าร่วมที่แตกต่างกันเล็กน้อย (ขอบคุณ @ypercube)

  CREATE TYPE dbo.optionB AS TABLE
  (
    LowerBound int,
    UpperBound int,
    PRIMARY KEY (LowerBound, UpperBound)
  );
  GO

  CREATE PROCEDURE dbo.procedure_optionB
    @t dbo.OptionB READONLY
  AS
  BEGIN
    SET NOCOUNT ON;
    SELECT <cols> FROM dbo.<table> AS t
      INNER JOIN @t AS tvp
      ON  t.entry >= tvp.LowerBound 
      AND t.entry <= tvp.UpperBound;
  END
  GO

6

ไม่มันไม่สามารถกำหนดค่าได้และคุณไม่สามารถเพิ่มสิ่งนี้ให้สูงขึ้นได้

มีวิธีแก้ปัญหาที่แนะนำในบทความ MSDN ที่คุณพูดถึงและบทความอื่น ๆ ฉันพูดถึงสองที่นี่ แต่คุณสามารถค้นหาเพิ่มเติมได้


4

ฉันแค่ 2 ¢เกี่ยวกับการทำให้เงื่อนไขการสืบค้นสั้นลง: -

หากคุณสามารถกำหนดค่าที่เป็นไปได้ทั้งหมดentryล่วงหน้าจะเป็นไปได้ไหมถ้าคุณใช้ส่วนเสริมของแบบสอบถาม

ก่อน

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

หลังจาก

where entry not in (4,5,11,14)

0

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

  • การใช้where entry in (<list of 100.000 values>)มันช่างน่ากลัวทั้งในแง่ของประสิทธิภาพและการบำรุงรักษา
  • การใช้where entry in (select whatever from table)นั้นแทบไม่น่ากลัวเหมือนอย่างที่เคยเป็นมาก่อน อย่างไรก็ตามขึ้นอยู่กับทั้งตารางที่แปลกประหลาดและเครื่องมือ SQL มันอาจทำงานได้ดีแม้จะเป็นมะเร็งกระจกตา (อาจจะโอเคใน Oracle จะไม่อยู่ใน MySQL และจำไม่ได้เกี่ยวกับ MSSQL)
  • การใช้ PLSQL เพื่อให้ได้สิ่งนี้น่ากลัวมาก
  • ในความคิดของฉัน (โดยไม่ต้องรู้เลยเลย) คุณควรเขียนคำถามใหม่ดังนี้:

    • หากค่า 100.000 เหล่านั้นเหมือนกันเสมอโดยไม่ขึ้นอยู่กับแบบสอบถามที่เหลือคุณควรอัปโหลดค่าเหล่านั้นไปยังตาราง (table_fixed_values) และใช้

      SELECT * -- whatever you might need
      FROM
         table_a A
         LEFT JOIN table_fixed_values ON A.entry=B.entry
      WHERE B.entry IS NOT NULL
    • หากค่า 100.000 เหล่านั้นไม่เหมือนกันต้องมีตรรกะบางอย่างในการเลือกค่า 100,000 ค่าเหล่านั้นตรรกะที่คุณควรฝังในONแบบสอบถามก่อนหน้า


3
ทำไมLEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULLและไม่เทียบเท่าอ่านง่ายและง่ายขึ้นINNER JOIN table_fixed_values ON A.entry=B.entry?
ypercubeᵀᴹ

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