La query funziona più volte più lentamente quando si confronta la colonna BIT con 0 anziché 1

StackOverflow https://stackoverflow.com/questions/468641

  •  19-08-2019
  •  | 
  •  

Domanda

Uso una vista basata su una query complessa con 17 join (sia interno che sinistro / esterno destro) e sottoquery. Tutte le righe della vista vengono visualizzate in circa 5 secondi.

SELECT * FROM a_view;

Una delle colonne della vista ha il tipo BIT. E quando filtro le righe della vista confrontandola con 1, una query funziona di nuovo circa 5 secondi.

SELECT * FROM a_view WHERE c = 1;

Ma quando confronto questa colonna BIT con 0, una query funziona per circa 50 secondi (10 volte più lenta).

SELECT * FROM a_view WHERE c = 0;

Questa query che restituisce le stesse righe di risultati funziona come previsto per circa 10 secondi:

SELECT * FROM a_view 
EXCEPT
SELECT * FROM a_view WHERE c = 1;

Quindi mi chiedo perché il confronto con 0 o 'FALSE' richieda così tanto tempo? Qualche idea, per favore.

L'ordinamento su questo campo BIT è veloce. Anche il filtro per altre colonne è veloce.

È stato utile?

Soluzione

Di solito esiste più di un modo per eseguire una query che coinvolge join. Tutti i moderni RDBMS effettuano ricerche in diversi piani di join alla ricerca del piano migliore stimando i costi (CPU e tempi di accesso al disco) per ciascuno.

Il problema è che ogni ulteriore join in una query moltiplica il numero di possibili piani per un numero crescente, determinando una crescita a doppio fattoriale (peggio che esponenziale) del numero di piani da considerare come il numero di join aumenta. Per questo motivo, il DB deve limitare la ricerca da qualche parte, il che significa che i piani non ottimali vengono inevitabilmente scelti per le query che coinvolgono molti join.

Per riferimento, PostgreSQL interrompe la valutazione di tutti i possibili piani dopo 12 join per impostazione predefinita. SQL Server avrà sicuramente un limite simile. 17 join richiederebbero (2 * 13) * (2 * 14) * (2 * 15) * (2 * 16) * (2 * 17) volte il tempo necessario per valutare - è più che sufficiente per sopraffare qualsiasi RDBMS che ha mai esistito, o mai .

C'è anche il fatto di considerare che i DB stimano i costi in base a statistiche grezze, come il numero di valori distinti in una colonna e / o un elenco dei 10 valori più comuni in una colonna. Tutto ciò si aggiunge al fatto che, con l'aumentare del numero di join, la probabilità di scegliere la strategia di join migliore (o anche ragionevole) va molto in basso .

Perché devi unirti a 17 tavoli? Non è possibile semplificare il tuo schema DB?

Altri suggerimenti

il motore SQL Server SQL inserisce l'intera query SQL della vista all'interno dell'istruzione SQL che hai scritto sulla vista e quindi tenta di ottimizzarla.

Questo potrebbe portare a una situazione in cui con c = 0, le statistiche delle tabelle utilizzate mostrano che ci sono molte più righe corrispondenti al predicato rispetto a c = 1. Ad esempio, con c = 1, una tabella che contiene il campo c che è il centro dei join potrebbe restituire solo 5 righe corrispondenti, il che porta a un piano di esecuzione molto diverso rispetto a se la tabella restituisce 1 milione di righe (che è ad esempio la situazione per c = 0).

Quindi esamina i piani di esecuzione per entrambi. Esamina anche i risultati del profiler del server per entrambi, poiché con c = 0, è possibile che ci siano molte più letture rispetto a c = 1 e che vengano restituiti molti più risultati che con c = 1. La restituzione di tutte le righe potrebbe richiedere del tempo, quindi anche questo potrebbe essere il motivo per cui la query è più lenta.

So che questa è una domanda molto vecchia, ma se qualcuno sta ancora cercando una soluzione, puoi provare questa. Recentemente ho riscontrato lo stesso problema ed era su una query relativamente semplice, solo 3 tabelle in un join, la più grande delle quali con poco più di 1000 righe. Purtroppo non avevo i privilegi per visualizzare il piano esecutivo, quindi ho dovuto improvvisare.

select * from subquery_x;

molto veloce, finito praticamente all'istante (restituito solo circa 500 righe) come dovrebbe

select * from subquery_x where column_x = 1  

dtto, molto veloce, column_x è una colonna di bit non nulla

select * from subquery_x where column_x = 0
select * from subquery_x where column_x != 1

dovrebbe restituire circa 300 righe, entrambe sono molto, molto lente, infatti ci sono voluti diversi minuti !!

soluzione semplice (ma strana) - converti la colonna nella clausola where in tinyint

select * from subquery_x where convert(tinyint,column_x) = 0

So che questo potrebbe avere alcuni effetti collaterali sulle prestazioni, ma ha funzionato come un incantesimo, confrontando il valore della colonna convertita con 1 è stato anche molto veloce, quindi l'ho lasciato così (è stato utilizzato in un report con il valore confrontato fornito come parametro)

Se qualcuno sa perché questo accade, faccelo sapere, sospetto che sia un bug, ma chissà, potrebbe anche essere una brutta caratteristica :-)

Simile alla soluzione di horcic.roman ... Ho lanciato il valore o la colonna su BIT per un grande aumento in velocità.

myColumn = CAST(1 AS BIT)

CAST(myColumn AS BIT) = 1

Ovviamente abbastanza strano dato che la colonna è BIT NOT NULL.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top