Ich stelle fest, dass die Zeilenschätzungen für einen bestimmten Join häufig weit entfernt sind, wenn Tempdb-Ereignisse verschüttet werden (was zu langsamen Abfragen führt). Ich habe gesehen, dass bei Merge- und Hash-Joins Überlaufereignisse auftreten, die die Laufzeit häufig um das 3-fache bis 10-fache erhöhen. Diese Frage betrifft die Verbesserung der Zeilenschätzungen unter der Annahme, dass dadurch die Wahrscheinlichkeit von Verschüttungsereignissen verringert wird.
Tatsächliche Anzahl der Zeilen 40k.
Für diese Abfrage zeigt der Plan eine schlechte Zeilenschätzung (11,3 Zeilen):
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);
Für diese Abfrage zeigt der Plan eine gute Zeilenschätzung (56.000 Zeilen):
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);
Können Statistiken oder Hinweise hinzugefügt werden, um die Zeilenschätzungen für den ersten Fall zu verbessern? Ich habe versucht, Statistiken mit bestimmten Filterwerten hinzuzufügen (Eigenschaft = 2840), aber entweder konnte die Kombination nicht korrekt angezeigt werden oder sie wird möglicherweise ignoriert, da die ObjectId zum Zeitpunkt der Kompilierung unbekannt ist und möglicherweise einen Durchschnitt über alle ObjectIds auswählt.
Gibt es einen Modus, in dem die Testabfrage zuerst ausgeführt und dann zur Ermittlung der Zeilenschätzungen verwendet wird, oder muss sie blind fliegen?
Diese bestimmte Eigenschaft hat viele Werte (40 KB) für einige Objekte und Null für die überwiegende Mehrheit. Ich würde mich über einen Hinweis freuen, in dem die maximal erwartete Anzahl von Zeilen für einen bestimmten Join angegeben werden könnte. Dies ist ein allgemeines Problem, da einige Parameter möglicherweise dynamisch als Teil des Joins bestimmt werden oder besser in einer Ansicht platziert werden sollten (keine Unterstützung für Variablen).
Gibt es Parameter, die angepasst werden können, um die Wahrscheinlichkeit von Verschüttungen in Tempdb zu minimieren (z. B. minimaler Speicher pro Abfrage)? Ein robuster Plan hatte keinen Einfluss auf die Schätzung.
Edit 2013.11.06 : Antwort auf Kommentare und zusätzliche Informationen:
Hier sind die Bilder des Abfrageplans. Die Warnungen beziehen sich auf das Kardinalitäts- / Suchprädikat mit convert ():
Gemäß dem Kommentar von @Aaron Bertrand habe ich versucht, die convert () als Test zu ersetzen:
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);
Als seltsamer, aber erfolgreicher Punkt von Interesse erlaubte es auch, die Suche kurzzuschließen:
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);
Beide listen eine korrekte Schlüsselsuche auf, aber nur die ersten listen eine "Ausgabe" von ObjectId auf. Ich denke, das deutet darauf hin, dass der zweite tatsächlich kurzgeschlossen ist.
Kann jemand überprüfen, ob jemals einzeilige Sonden durchgeführt werden, um bei Zeilenschätzungen zu helfen? Es scheint falsch, die Optimierung auf nur Histogrammschätzungen zu beschränken, wenn eine einzeilige PK-Suche die Genauigkeit der Suche im Histogramm erheblich verbessern kann (insbesondere wenn Überlaufpotential oder Verlauf vorhanden sind). Wenn eine echte Abfrage 10 dieser Unterverknüpfungen enthält, werden sie im Idealfall parallel ausgeführt.
Eine Randnotiz: Da sql_variant seinen Basistyp (SQL_VARIANT_PROPERTY = BaseType) im Feld selbst speichert, würde ich erwarten, dass eine convert () nahezu kostenlos ist, solange sie "direkt" konvertierbar ist (z. B. keine Zeichenfolge in Dezimalzahl, sondern int in) int oder vielleicht int zu bigint). Da dies zur Kompilierungszeit nicht bekannt ist, aber dem Benutzer möglicherweise bekannt ist, würde eine Funktion "AssumeType (type, ...)" für sql_variants möglicherweise eine transparentere Behandlung ermöglichen.
declare @a bigint =
wie Sie es getan haben, scheint mir eine natürliche Lösung zu sein. Warum ist das nicht akzeptabel?
CONVERT()
in Spalten zu verwenden und sie dann zu verbinden. Dies ist in den meisten Fällen sicherlich nicht effizient. In diesem Fall muss nur ein Wert konvertiert werden. Dies ist wahrscheinlich kein Problem, aber welche Indizes haben Sie in der Tabelle? EAV-Designs funktionieren normalerweise nur bei ordnungsgemäßer Indizierung gut (was viele Indizes in den normalerweise engen Tabellen bedeutet).