Errore 8632 di SQL Server a causa di oltre 100.000 voci nella clausola WHERE


16

Il mio problema (o almeno il messaggio di errore) è molto simile al Query Processor a corto di risorse interne - query sql estremamente lunga .

Il mio cliente sta lavorando con una query di selezione SQL, contenente una clausola where con esattamente 100.000 voci.

La query non riesce con errore 8632 e messaggio di errore

Errore interno: è stato raggiunto un limite di servizi di espressione. Cerca espressioni potenzialmente complesse nella tua query e prova a semplificarle.)

Trovo molto singolare che questo messaggio di errore venga lanciato, esattamente a 100.000 voci, quindi mi chiedo se questo sia un valore configurabile. È questo il caso e in caso affermativo, come posso aumentare questo valore a uno più alto?

Su MSDN , c'è la proposta di riscrivere la query, ma vorrei evitarlo.

Nel frattempo ho scoperto che l'elenco delle voci di cui sto parlando contiene numeri naturali, alcune di esse sembrano essere sequenziali (qualcosa del tipo (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).

Questo rende la clausola where SQL qualcosa di simile a:

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

Potrei trasformarlo in:

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

Questo può essere abbreviato da:

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

... o qualcosa di simile? (So ​​che è un colpo lungo, ma renderebbe gli aggiornamenti del software più facili e più leggibili)

Per tua informazione: i dati nella clausola where sono il risultato di un calcolo, fatto su un'altra tabella: prima le voci di quella tabella vengono lette e filtrate all'inizio, quindi viene eseguita un'elaborazione aggiuntiva (che è impossibile fare usando SQL), il risultato di tale elaborazione aggiuntiva è più filtrante e il risultato viene utilizzato nella clausola where. Poiché era impossibile scrivere il filtro completo in SQL, è stato utilizzato il metodo menzionato. Ovviamente il contenuto della clausola where potrebbe cambiare ad ogni elaborazione, quindi la necessità di una soluzione dinamica.


7
In risposta alla tua modifica: no, WHERE INnon supporta questo tipo di sintassi dell'intervallo. Inoltre, non dovrebbe essere WHERE () OR () OR ()AND. Ma per usare il suggerimento di Brent, in realtà non devi cambiare l'intera query, puoi semplicemente farlo WHERE IN (SELECT myID FROM #biglist). E #biglistpotrebbe essere un vero tavolo (permanente) o un tavolo temporaneo che si crea al volo.
BradC,

Per favore, per favore, inserisci l'intera query e ciò che stai calcolando esternamente, questo è probabilmente qualcosa che potresti fare completamente in SQL. Rinomina i nomi dei campi se sei preoccupato per la privacy o altro.
Mike,

Risposte:


67

Per cercare più di 100.000 valori, inseriscili invece in una tabella temporanea, una riga per valore che stai cercando. Quindi, unisci la tua query a quella tabella temporanea per il filtro.

Qualcosa con più di 100.000 valori non è un parametro, è una tabella. Piuttosto che pensare di aumentare il limite, considera la regola del dieci percento di Swart : se ti stai avvicinando al 10% di un limite di SQL Server, probabilmente avrai un brutto momento.


1
I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Paul White Ripristina Monica

10

Se hai intenzione di cambiare l'app in ogni caso, considera entrambi

(a) usando un TVP per l'intero insieme di valori - creeresti un DataTablein C # e lo passeresti in una procedura memorizzata usando StructuredType, come dimostrerò qui . (Si spera che 100.000 voci non siano normali, poiché la scalabilità potrebbe essere un problema, indipendentemente dall'approccio utilizzato.)

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

o

(b) usare un TVP per superare i limiti superiore e inferiore degli intervalli e scrivere un join leggermente diverso (grazie a @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


4

Solo il mio 2 ¢ per quanto riguarda la riduzione della condizione della query: -

Se sei in grado di determinare entryin anticipo tutti i possibili valori , sarebbe possibile prendere il complemento della tua query?

Prima

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

Dopo

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

0

È difficile capire cosa stai cercando di realizzare senza essere effettivamente in grado di vedere la query, ma eccoci qui, supponendo che la tua query abbia bisogno solo di due tabelle, una con i dati, una con i valori che vuoi evitare:

  • utilizzando where entry in (<list of 100.000 values>) è scandalosamente terribile, sia dal punto di vista dell'efficienza che della manutenzione.
  • utilizzando where entry in (select whatever from table) è quasi atrocemente terribile come quello precedente. Tuttavia, a seconda dell'idiosincrasia di entrambe le tabelle e del motore SQL, potrebbe funzionare bene nonostante il cancro alla cornea. (Potrebbe essere OK in Oracle, non sarà mai in MySQL, non ricordo MSSQL).
  • L'utilizzo di PLSQL per raggiungere questo obiettivo è semplicemente orrendo.
  • Secondo me (senza conoscere la query), dovresti riscrivere la query come segue:

    • Se questi 100.000 valori sono sempre gli stessi, non basandosi sul resto della query, è necessario caricare tali valori su una tabella (table_fixed_values) e utilizzare

      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
    • Se quei 100.000 valori non sono gli stessi, ci deve essere un qualche tipo di logica per raccogliere quei 100.000 valori, logica che dovresti incorporare nella ONquery precedente.


3
Perché LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULLe non l'equivalente, più semplice e più facile da leggere INNER JOIN table_fixed_values ON A.entry=B.entry?
ypercubeᵀᴹ

@ ypercubeᵀᴹ completamente giusto, l'INNER JOIN ha più senso.
Glezo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.