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


16

Mon problème (ou du moins le message d'erreur) est très similaire au processeur de requêtes à court 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?

Sur 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.


7
En réponse à votre modification: non, WHERE INne prend pas en charge ce type de syntaxe de plage. De plus, ce ne devrait WHERE () OR () OR ()pas être ET. Mais pour utiliser la suggestion de Brent, vous n'avez pas réellement à modifier la requête entière, vous pouvez simplement le faire WHERE IN (SELECT myID FROM #biglist). Et #biglistpeut être soit une vraie table (permanente), soit une table temporaire que vous créez à la volée.
BradC

Veuillez s'il vous plaît s'il vous plaît poster la requête entière et ce que vous calculez en externe, c'est probablement vraiment quelque chose que vous pourriez faire complètement en SQL. Renommez les noms de champs si vous êtes préoccupé par la confidentialité ou autre.
Mike

Réponses:


67

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, considérez la 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.


1
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Paul White réintègre Monica

10

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

(a) en utilisant un TVP pour l'ensemble des valeurs - vous créeriez un DataTableen 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

6

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.


4

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

Si vous êtes en mesure de déterminer entryà l'avance toutes les valeurs possibles , 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)

0

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 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 where entry in (select whatever from table)est à peine aussi atrocement horrible que la précédente. Pourtant, selon l'idiosyncrasie des deux tables et le moteur SQL, il pourrait 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 devez réécrire la requête comme suit:

    • Si ces 100 000 valeurs sont toujours les mêmes, sans s'appuyer 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 la ONrequête précédente.


3
Pourquoi LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULLet non l'équivalent, plus simple et plus facile à lire INNER JOIN table_fixed_values ON A.entry=B.entry?
ypercubeᵀᴹ

@ ypercubeᵀᴹ complètement à droite, le INNER JOIN a plus de sens.
glezo
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.