Помилка SQL Server 8632 через понад 100 000 записів у пункті WHERE


16

Моя проблема (або принаймні повідомлення про помилку) дуже схожа на запит процесора, у якого не вистачало внутрішніх ресурсів - надзвичайно довгий запит sql .

Мій клієнт працює з запитом на вибір SQL, що містить пункт де саме 100 000 записів.

Помилка запиту з помилкою 8632 та повідомленням про помилку

Внутрішня помилка: досягнуто ліміту служб вираження даних. Будь ласка, знайдіть у запиті потенційно складні вирази та спробуйте їх спростити.)

Я вважаю дуже своєрідним, що це повідомлення про помилку викидається саме на 100 000 записів, тому мені цікаво, чи це налаштоване значення. Це так і якщо так, то як я можу збільшити це значення до більш високого?

У MSDN є пропозиція переписати запит, але я хотів би цього уникнути.

Тим часом я з’ясував, що список записів, про які я говорю, містить натуральні числа, досить багато з них здаються послідовними (щось на зразок (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).

Це робить SQL-де-пунктом щось на зразок:

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 є результатом обчислення, зробленого в іншій таблиці: спочатку записи цієї таблиці читаються та фільтруються на початку, потім виконується деяка додаткова обробка (що неможливо зробити за допомогою SQL), результатом цієї додаткової обробки є більше фільтрування, і результат цього використовується в пункті де-де. Оскільки неможливо було написати повну фільтрацію в SQL, був використаний згаданий метод. Очевидно, що зміст де-пункту може змінюватися при кожній обробці, отже, необхідність динамічного рішення.


7
У відповідь на вашу редагування: ні, WHERE INне підтримує такий синтаксис діапазону. Також це не повинно бути WHERE () OR () OR ()ТА. Але, щоб використовувати пропозицію Brent, вам насправді не потрібно змінювати весь запит, ви могли просто зробити WHERE IN (SELECT myID FROM #biglist). І #biglistможе бути або справжнім (постійним) столом, або тимчасовою таблицею, яку ви робите на льоту.
BradC

Будь ласка, будь ласка, опублікуйте весь запит і те, що ви обчислюєте зовні, це, мабуть, справді щось, що ви могли б зробити повністю в SQL. Перейменуйте імена полів, якщо вас турбує конфіденційність чи інше.
Майк

Відповіді:


67

Щоб шукати більше 100 000 значень, помістіть їх у тимчасову таблицю, по одному рядку на значення, яке ви шукаєте. Потім приєднайте запит до цієї темп-таблиці для фільтрації.

Щось із більш ніж 100 000 значеннями не є параметром - це таблиця. Замість того, щоб думати про підвищення ліміту, врахуйте десять відсоткових правил Swart : якщо ви наближаєтесь до 10% ліміту SQL Server, ви, ймовірно, будете погано провести час.


1
Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Моніка Пола Білого відновлює

10

Якщо ви все одно хочете змінити додаток, врахуйте будь-яке

(a) використовуючи TVP для всього набору значень - ви створили DataTableC # і передали його в збережену процедуру, використовуючи 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ᵀᴹ абсолютно прав, ВНУТРІШНЕ ПРИЄДНАННЯ має більше сенсу.
глезо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.