2016-01-07 6 views
5

Aggiornamento: riceverò il piano di query non appena possibile.L'aggiunta di Condizionale attorno alla query aumenta il tempo di oltre il 2400%

Abbiamo avuto una query con scarse prestazioni che ha richiesto 4 minuti per una particolare organizzazione. Dopo la consueta ricompilazione delle statistiche memorizzate di proc e di aggiornamento non è stato d'aiuto, abbiamo riscritto il if Exists (...) in un conteggio selezionato (*) ... e la stored procedure da 4 minuti a 70 millisecondi. Qual è il problema con il condizionale che rende una query di 70 ms impiega 4 minuti? Vedi gli esempi

Questi tutti prendono 4+ minuti:

if (
    SELECT COUNT(*)  
    FROM ObservationOrganism omo 
    JOIN Observation   om ON om.ObservationID = omo.ObservationMicID 
    JOIN Organism    o ON o.OrganismID  = omo.OrganismID 
    JOIN ObservationMicDrug omd ON omd.ObservationOrganismID = omo.ObservationOrganismID 
    JOIN SIRN     srn ON srn.SIRNID  = omd.SIRNID 
    JOIN OrganismDrug   od ON od.OrganismDrugID = omd.OrganismDrugID 
    WHERE 
    om.StatusCode IN ('F', 'C') 
    AND o.OrganismGroupID <> -1 
    AND od.OrganismDrugGroupID <> -1 
    AND (om.LabType <> 'screen' OR om.LabType IS NULL)) > 0 

print 'records';  

-

IF (EXISTS(
    SELECT *  
    FROM ObservationOrganism omo 
    JOIN Observation   om ON om.ObservationID = omo.ObservationMicID 
    JOIN Organism    o ON o.OrganismID  = omo.OrganismID 
    JOIN ObservationMicDrug omd ON omd.ObservationOrganismID = omo.ObservationOrganismID 
    JOIN SIRN     srn ON srn.SIRNID  = omd.SIRNID 
    JOIN OrganismDrug   od ON od.OrganismDrugID = omd.OrganismDrugID 
    WHERE 
    om.StatusCode IN ('F', 'C') 
    AND o.OrganismGroupID <> -1 
    AND od.OrganismDrugGroupID <> -1 
    AND (om.LabType <> 'screen' OR om.LabType IS NULL)) 

print 'records' 

Tutto questo prende 70 millisecondi:

Declare @recordCount INT; 
SELECT @recordCount = COUNT(*)  
    FROM ObservationOrganism omo 
    JOIN Observation   om ON om.ObservationID = omo.ObservationMicID 
    JOIN Organism    o ON o.OrganismID  = omo.OrganismID 
    JOIN ObservationMicDrug omd ON omd.ObservationOrganismID = omo.ObservationOrganismID 
    JOIN SIRN     srn ON srn.SIRNID  = omd.SIRNID 
    JOIN OrganismDrug   od ON od.OrganismDrugID = omd.OrganismDrugID 
    WHERE 
    om.StatusCode IN ('F', 'C') 
    AND o.OrganismGroupID <> -1 
    AND od.OrganismDrugGroupID <> -1 
    AND (om.LabType <> 'screen' OR om.LabType IS NULL); 

IF(@recordCount > 0) 
    print 'records'; 

Non ha senso per me perché spostare la stessa query Count(*) in un'istruzione if provoca una tale degradazione o perché "Esiste" è più lento di Count. Ho anche provato il exists() in un select CASE WHEN Exists() ed è ancora 4+ minuti.

+0

Hai esaminato i piani di query? –

+0

Puoi pubblicare i piani di query tra i tre esempi? –

+0

typo in last query? Una parentesi non chiusa in 'AND (om.LabType <> 'schermo' O om.LabType È NULL;' – Ingaz

risposta

3

Dato che la mia risposta precedente è stata menzionata, proverò a spiegare di nuovo perché queste cose sono piuttosto complicate. Quindi sì, penso che tu stia vedendo lo stesso problema di the other question. Vale a dire un problema row goal.

Quindi, per cercare di spiegare che cosa sta causando questo, inizierò con i tre tipi di join che sono a disposizione del motore (e in modo abbastanza categorico): Loop Joins, Merge Joins, Hash Joins. I join dei loop sono ciò che suonano, un ciclo annidato su entrambi i gruppi di dati. Unisci join prende due elenchi ordinati e li sposta in blocco. E Hash si unisce a buttare tutto nel set più piccolo in uno schedario e quindi cercare gli articoli nel set più grande una volta che lo schedario è stato riempito.

Quindi, per quanto riguarda le prestazioni, i loop join non richiedono praticamente alcuna configurazione e se si sta cercando solo una piccola quantità di dati sono davvero ottimali. L'unione è il meglio del meglio per unire le prestazioni per qualsiasi dimensione di dati, ma richiede che i dati siano già ordinati (il che è raro). Gli hash join richiedono una buona dose di configurazione, ma consentono di unire rapidamente set di dati di grandi dimensioni.

Ora arriviamo alla tua richiesta e alla differenza tra COUNT(*) e EXISTS/TOP 1. Quindi il comportamento che stai vedendo è che l'ottimizzatore pensa che le righe di questa query siano realmente verosimili (puoi confermarlo pianificando la query senza raggruppare e vedere quanti record pensa che otterranno nell'ultimo passaggio). In particolare, probabilmente pensa che per qualche tabella in quella query, ogni record in quella tabella apparirà nell'output.

"Eureka!" dice "se tutte le righe di questa tabella finiscono nell'output, per scoprire se ne esiste una posso fare il join del ciclo di avvio veramente economico dappertutto perché anche se è lento per i grandi set di dati, ho solo bisogno di una riga". Ma poi non trova quella riga. E non lo trova più. E ora sta iterando attraverso una vasta serie di dati utilizzando i mezzi meno efficienti a sua disposizione per diserbo attraverso grandi serie di dati.

In confronto, se si richiede il conteggio completo dei dati, deve trovare ogni record per definizione. Vede una vasta serie di dati e sceglie le scelte migliori per l'iterazione attraverso l'intero insieme di dati invece di una piccola porzione di esso.

Se, d'altra parte, era davvero corretto e i record erano molto ben correlati avrebbe trovato il tuo record con il minor numero possibile di risorse del server e massimizzato il suo throughput complessivo.

+0

grazie per la spiegazione. Leggendo questo e altri post, il modo per correggere correttamente questo è creare alcuni indici mancanti o riordinare i join, ecc ... È corretto? –

+0

Non c'è davvero alcuna garanzia sui modi per fare in modo che il server faccia quello che vuoi. Gli indici possono essere overkill a meno che non si abbiano molte altre query che desiderano quei campi. Puoi provare a cambiare tutti i tuoi JOIN al più esplicito INNER HASH JOIN (o al sottoinsieme minimo che ti dà il comportamento che desideri) e il motore darà maggiore preferenza ai join hash, ma anche in questo caso può comunque ignorare i tuoi suggerimenti pensa che ci sia un modo migliore. Suggerisco semplicemente di dichiarare la variabile, spostando il conteggio (*) fuori dalle condizioni e procedendo personalmente mentre combattere il motore è una battaglia in salita. –

+0

Grazie per i suggerimenti. L'ho modificato in un Conteggio selezionato (*), fatto una meditazione e continuo a cercare la pace interiore ogni volta che leggo articoli sul motivo per cui non "usi" mai Count (*) per verificare l'esistenza. Penso di essermi finalmente trasferito :) –

Problemi correlati