web-dev-qa-db-fra.com

Erreur SQL Server 8632 en raison de plus de 100 000 entrées dans la clause WHERE

Mon problème (ou au moins le message d'erreur) est très similaire à le processeur de requêtes a manqué de ressources internes - requête SQL extrêmement longue .

Mon client travaille avec une requête de sélection SQL, contenant une clause where avec exactement 100 000 entrées.

La requête échoue avec l'erreur 8632 et le message d'erreur

Erreur interne: une limite de services d'expression a été atteinte. Veuillez rechercher des expressions potentiellement complexes dans votre requête et essayez de les simplifier.)

Je trouve très particulier que ce message d'erreur soit lancé, exactement à 100 000 entrées, donc je me demande si c'est une valeur configurable. Est-ce le cas et si oui, comment puis-je augmenter cette valeur à une valeur plus élevée?

Le MSDN , il est proposé de réécrire la requête, mais j'aimerais éviter cela.

Pendant ce temps, j'ai découvert que la liste des entrées dont je parle contient des nombres naturels, certains d'entre eux semblent être séquentiels (quelque chose comme (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).

Cela fait de la clause where SQL quelque chose comme:

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

Je pourrais transformer cela en:

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

Cela peut-il être raccourci par:

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

... ou quelque chose de similaire? (Je sais que c'est long, mais cela rendrait les mises à jour logicielles plus faciles et plus lisibles)

Pour votre information: les données de la clause where sont le résultat d'un calcul effectué sur une autre table: d'abord les entrées de cette table sont lues et filtrées au début, puis un traitement supplémentaire est effectué (ce qui est impossible à faire en utilisant SQL), le résultat de ce traitement supplémentaire est plus de filtrage et le résultat est utilisé dans la clause where. Comme il était impossible d'écrire le filtrage complet en SQL, la méthode mentionnée a été utilisée. De toute évidence, le contenu de la clause where peut changer à chaque traitement, d'où la nécessité d'une solution dynamique.

16
Dominique

Pour rechercher plus de 100 000 valeurs, placez-les plutôt dans une table temporaire, une ligne par valeur que vous recherchez. Ensuite, joignez votre requête à cette table temporaire pour le filtrage.

Quelque chose avec plus de 100 000 valeurs n'est pas un paramètre - c'est une table. Plutôt que de penser à augmenter la limite, pensez à règle de dix pour cent de Swart : si vous approchez de 10% d'une limite SQL Server, vous allez probablement passer un mauvais moment.

67
Brent Ozar

Si vous voulez quand même changer l'application, pensez à

(a) en utilisant un TVP pour l'ensemble des valeurs - vous créeriez un DataTable en C # et le passeriez dans une procédure stockée en utilisant StructuredType, comme je le démontre ici =. (Espérons que 100 000 entrées ne sont pas normales, car l'évolutivité peut être un problème quelle que soit l'approche que vous utilisez.)

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

ou

(b) utiliser un TVP pour passer dans les limites supérieures et inférieures des plages, et écrire une jointure légèrement différente (merci @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
10
Aaron Bertrand

Non, il n'est pas configurable et vous ne pouvez pas l'augmenter à un niveau supérieur.

Il existe une solution de contournement suggérée dans l'article MSDN que vous avez mentionné et dans d'autres articles. J'en ai mentionné deux ici mais vous pouvez en chercher plus.

6
SqlWorldWide

Juste mon 2 ¢ concernant la réduction de la condition de requête: -

Si vous êtes en mesure de déterminer à l'avance toutes les valeurs possibles de entry, serait-il possible si vous preniez le complément de votre requête?

Avant

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

Après

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

Il est difficile de comprendre ce que vous essayez d'accomplir sans être en mesure de voir la requête, mais nous y allons, en supposant que votre requête n'a besoin que de deux tables, une avec les données, une avec les valeurs que vous souhaitez éviter:

  • L'utilisation de where entry in (<list of 100.000 values>) est scandaleusement horrible, tant du point de vue de l'efficacité que de la maintenance.
  • L'utilisation de where entry in (select whatever from table) est à peine aussi horrible que la précédente. Pourtant, selon l'idiosyncrasie des deux tables et le moteur SQL, il peut fonctionner correctement malgré le cancer de la cornée. (Pourrait être OK dans Oracle, ne sera jamais dans MySQL, je ne me souviens pas de MSSQL).
  • Utiliser PLSQL pour y parvenir est tout simplement horrible.
  • À mon avis (sans connaître la requête du tout), vous devriez réécrire la requête comme suit:

    • Si ces 100 000 valeurs sont toujours identiques, ne reposant pas sur le reste de la requête, vous devez télécharger ces valeurs dans une table (table_fixed_values) et utiliser

      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
      
    • Si ces 100 000 valeurs ne sont pas les mêmes, il doit y avoir une sorte de logique pour récupérer ces 100 000 valeurs, logique que vous devez intégrer dans le ON de la requête précédente.

0
glezo