Lỗi máy chủ SQL 8632 do có hơn 100.000 mục trong mệnh đề WHERE


16

Vấn đề của tôi (hoặc ít nhất là thông báo lỗi) rất giống với bộ xử lý truy vấn đã hết tài nguyên nội bộ - truy vấn sql cực dài .

Khách hàng của tôi đang làm việc với truy vấn chọn SQL, chứa mệnh đề where với chính xác 100.000 mục nhập.

Truy vấn không thành công với lỗi 8632 và thông báo lỗi

Lỗi nội bộ: Đã đạt đến giới hạn dịch vụ biểu thức. Vui lòng tìm các biểu thức có khả năng phức tạp trong truy vấn của bạn và cố gắng đơn giản hóa chúng.)

Tôi thấy rất đặc biệt khi thông báo lỗi này bị ném, chính xác là 100.000 mục, vì vậy tôi tự hỏi liệu đây có phải là một giá trị cấu hình không. Đây có phải là trường hợp và trong trường hợp có, làm thế nào tôi có thể tăng giá trị này lên cao hơn?

Trên MSDN , có đề xuất viết lại truy vấn, nhưng tôi muốn tránh điều này.

Trong khi đó, tôi phát hiện ra rằng danh sách các mục tôi đang nói có chứa số tự nhiên, khá nhiều trong số chúng dường như là tuần tự (đại loại như (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).

Điều này làm cho mệnh đề SQL ở đâu đó như:

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

Tôi có thể chuyển đổi nó thành:

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

Điều này có thể được rút ngắn bằng cách:

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

... Hoặc một cái gì đó tương tự? (Tôi biết đó là một cú sút xa, nhưng nó sẽ giúp cập nhật phần mềm dễ dàng hơn và dễ đọc hơn)

Đối với thông tin của bạn: dữ liệu trong mệnh đề where là kết quả của phép tính, được thực hiện trên một bảng khác: đầu tiên các mục của bảng đó được đọc và lọc ở đầu, sau đó một số xử lý bổ sung được thực hiện (không thể thực hiện được bằng cách sử dụng SQL), kết quả của quá trình xử lý bổ sung đó là lọc nhiều hơn và kết quả của việc đó được sử dụng trong mệnh đề where. Vì không thể viết bộ lọc hoàn chỉnh trong SQL, phương thức được đề cập đã được sử dụng. Rõ ràng nội dung của mệnh đề where có thể thay đổi ở mỗi lần xử lý, do đó cần một giải pháp động.


7
Đáp lại chỉnh sửa của bạn: không, WHERE INkhông hỗ trợ loại cú pháp phạm vi đó. Ngoài ra, nó không nên WHERE () OR () OR ()VÀ. Nhưng để sử dụng đề xuất của Brent, bạn thực sự không phải thay đổi toàn bộ truy vấn, bạn chỉ có thể làm WHERE IN (SELECT myID FROM #biglist). Và #biglistcó thể là một bảng thực sự (vĩnh viễn) hoặc một bảng tạm thời bạn thực hiện khi đang bay.
BradC

Xin vui lòng xin vui lòng gửi toàn bộ truy vấn và những gì bạn đang tính toán bên ngoài, đây có lẽ thực sự là điều bạn có thể làm trong SQL hoàn toàn. Đổi tên trường nếu bạn quan tâm đến quyền riêng tư hoặc bất cứ điều gì.
Mike

Câu trả lời:


67

Để tìm kiếm hơn 100.000 giá trị, thay vào đó hãy đặt chúng vào bảng tạm thời, một hàng cho mỗi giá trị mà bạn đang tìm kiếm. Sau đó, tham gia truy vấn của bạn vào bảng tạm thời đó để lọc.

Một cái gì đó có hơn 100.000 giá trị không phải là một tham số - đó là một bảng. Thay vì suy nghĩ về việc tăng giới hạn, hãy xem xét Quy tắc mười phần trăm của Swart : nếu bạn đạt gần 10% giới hạn SQL Server, có lẽ bạn sẽ có một khoảng thời gian tồi tệ.


1
Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Paul White phục hồi Monica

10

Nếu bạn vẫn muốn thay đổi ứng dụng, hãy xem xét một trong hai

(a) sử dụng TVP cho toàn bộ các giá trị - bạn sẽ tạo một DataTableC # và chuyển nó vào một thủ tục được lưu trữ bằng cách sử dụng StructuredType, như tôi trình bày ở đây . (Hy vọng 100.000 mục không bình thường, vì khả năng mở rộng có thể là một vấn đề cho dù bạn sử dụng phương pháp nào.)

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

hoặc là

(b) sử dụng TVP để vượt qua giới hạn trên và dưới của phạm vi và viết một liên kết hơi khác (cảm ơn @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

Không, nó không thể cấu hình và bạn không thể tăng mức này lên mức cao hơn.

Có cách giải quyết được đề xuất trong bài viết MSDN mà bạn đã đề cập và các bài viết khác. Tôi đã đề cập đến hai ở đây nhưng bạn có thể tìm kiếm thêm.


4

Chỉ cần 2 của tôi về việc làm cho điều kiện truy vấn ngắn hơn: -

Nếu bạn có thể xác định tất cả các giá trị có thể có entrytrước, liệu có khả thi nếu bạn thực hiện bổ sung cho truy vấn của mình không?

Trước

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

Sau

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

0

Thật khó để tìm ra những gì bạn đang cố gắng thực hiện mà không thực sự có thể nhìn thấy truy vấn, nhưng ở đây chúng tôi đi, giả sử rằng truy vấn của bạn chỉ cần hai bảng, một bảng có dữ liệu, một bảng có các giá trị bạn muốn tránh:

  • Sử dụng where entry in (<list of 100.000 values>)là hết sức khủng khiếp, cả từ quan điểm hiệu quả và bảo trì.
  • Sử dụng where entry in (select whatever from table)là hầu như khủng khiếp khủng khiếp như trước đây. Tuy nhiên, tùy thuộc vào cả hai bảng idiosyncrasy và công cụ SQL, nó có thể hoạt động tốt mặc dù ung thư giác mạc. (Có thể OK trong Oracle, sẽ không bao giờ có trong MySQL, không thể nhớ về MSSQL).
  • Sử dụng PLQuery để đạt được điều này chỉ đơn giản là khủng khiếp.
  • Theo ý kiến ​​của tôi (hoàn toàn không biết truy vấn), bạn nên viết lại truy vấn như sau:

    • Nếu 100.000 giá trị đó luôn giống nhau, không phụ thuộc vào phần còn lại của truy vấn, bạn nên tải các giá trị đó lên một bảng (table_fixed_values) và sử dụng

      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
    • Nếu 100.000 giá trị đó không giống nhau, thì phải có một số loại logic để chọn 100.000 giá trị đó, logic mà bạn nên nhúng vào ONtruy vấn trước đó.


3
Tại sao LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULLvà không tương đương, đơn giản và dễ đọc hơn INNER JOIN table_fixed_values ON A.entry=B.entry?
ypercubeᵀᴹ

@ ypercubeᵀᴹ hoàn toàn đúng, INNER THAM GIA có ý nghĩa hơn.
glezo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.