Ho notato che quando si verificano eventi di tipo tempdb (che causano query lente), spesso le stime delle righe sono molto lontane per un particolare join. Ho visto eventi di spill avvenire con merge e hash join e spesso aumentano il tempo di esecuzione da 3x a 10x. Questa domanda riguarda come migliorare le stime delle righe partendo dal presupposto che ridurrà le possibilità di eventi di fuoriuscita.
Numero effettivo di file 40k.
Per questa query, il piano mostra una stima delle righe errate (11,3 righe):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Per questa query, il piano mostra una buona stima delle righe (56k righe):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
È possibile aggiungere statistiche o suggerimenti per migliorare le stime delle righe per il primo caso? Ho provato ad aggiungere statistiche con valori di filtro particolari (proprietà = 2840) ma non è stato possibile ottenere la combinazione corretta o forse viene ignorata perché l'ObjectId è sconosciuto al momento della compilazione e potrebbe scegliere una media su tutti gli ObjectId.
Esiste una modalità in cui esegua prima la query del probe e quindi la utilizza per determinare le stime delle righe o deve volare alla cieca?
Questa particolare proprietà ha molti valori (40k) su pochi oggetti e zero sulla stragrande maggioranza. Sarei felice con un suggerimento in cui è possibile specificare il numero massimo di righe previsto per un determinato join. Questo è un problema generalmente inquietante perché alcuni parametri possono essere determinati dinamicamente come parte del join o potrebbero essere posizionati meglio all'interno di una vista (nessun supporto per le variabili).
Esistono parametri che possono essere regolati per ridurre al minimo la possibilità di fuoriuscite in tempdb (ad es. Memoria minima per query)? Il piano robusto non ha avuto alcun effetto sulla stima.
Modifica 2013.11.06 : Risposta a commenti e informazioni aggiuntive:
Ecco le immagini del piano di query. Gli avvertimenti riguardano il predicato cardinalità / cercare con convert ():
Per il commento di @Aaron Bertrand, ho provato a sostituire convert () come test:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
Come punto di interesse strano ma di successo, ha anche permesso di cortocircuitare la ricerca:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Entrambi elencano una corretta ricerca dei tasti ma solo i primi elencano un "Output" di ObjectId. Immagino che indichi che il secondo è davvero un corto circuito?
Qualcuno può verificare se vengono mai eseguite sonde a riga singola per aiutare con le stime delle righe? Sembra sbagliato limitare l'ottimizzazione alle stime dell'istogramma solo quando una ricerca PK a riga singola può migliorare notevolmente l'accuratezza della ricerca nell'istogramma (specialmente se esiste un potenziale di fuoriuscita o cronologia). Quando ci sono 10 di questi sub-join in una vera query, idealmente dovrebbero accadere in parallelo.
Una nota a margine, dal momento che sql_variant memorizza il suo tipo di base (SQL_VARIANT_PROPERTY = BaseType) all'interno del campo stesso, mi aspetterei che un convert () sia quasi privo di costi fintanto che è "direttamente" convertibile (es. Non stringa in decimale ma piuttosto int per int o forse int to bigint). Poiché ciò non è noto al momento della compilazione ma può essere noto all'utente, forse una funzione "AssumeType (tipo, ...)" per sql_variants consentirebbe loro di essere trattati in modo più trasparente.
declare @a bigint =
come hai fatto mi sembra una soluzione naturale, perché è inaccettabile?
CONVERT()
nelle colonne e poi unirle. Questo non è certamente efficace nella maggior parte dei casi. In questo particolare, è solo un valore da convertire, quindi probabilmente non è un problema ma quali indici hai sul tavolo? I progetti EAV di solito funzionano bene, solo con una corretta indicizzazione (il che significa molti indici nelle tabelle solitamente strette).