Ich habe eine große Ansicht, die ich innerhalb einer Anwendung verwende. Ich glaube, ich habe mein Leistungsproblem eingegrenzt, bin mir aber nicht sicher, wie ich es beheben soll. Eine vereinfachte Version der Ansicht sieht folgendermaßen aus:
SELECT ISNULL(SEId + '-' + PEId, '0-0') AS Id,
*,
DATEADD(minute, Duration, EventTime) AS EventEndTime
FROM (
SELECT se.SEId, pe.PEId,
COALESCE(pe.StaffName, se.StaffName) AS StaffName, -- << Problem!
COALESCE(pe.EventTime, se.EventTime) AS EventTime,
COALESCE(pe.EventType, se.EventType) AS EventType,
COALESCE(pe.Duration, se.Duration) AS Duration,
COALESCE(pe.Data, se.Data) AS Data,
COALESCE(pe.Field, se.Field) AS Field,
pe.ThisThing, se.OtherThing
FROM PE pe FULL OUTER JOIN SE se
ON pe.StaffName = se.StaffName
AND pe.Duration = se.Duration
AND pe.EventTime = se.EventTime
WHERE NOT(pe.ThisThing = 1 AND se.OtherThing = 0)
) Z
Das rechtfertigt wahrscheinlich nicht den ganzen Grund für die Abfragestruktur, gibt Ihnen aber vielleicht eine Idee - diese Ansicht verbindet zwei sehr schlecht gestaltete Tabellen, über die ich keine Kontrolle habe, und versucht, einige Informationen daraus zu synthetisieren.
Da dies eine Ansicht ist, die von der Anwendung verwendet wird, verpacke ich sie beim Optimieren in ein anderes SELECT wie folgt:
SELECT * FROM (
-- … above code …
) Q
WHERE StaffName = 'SMITH, JOHN Q'
weil die Anwendung im Ergebnis nach bestimmten Mitarbeitern sucht.
Das Problem scheint der COALESCE(pe.StaffName, se.StaffName) AS StaffName
Abschnitt zu sein, den ich aus der Ansicht auf auswähle StaffName
. Wenn ich das auf pe.StaffName AS StaffName
oder ändere, se.StaffName AS StaffName
verschwinden die Leistungsprobleme (siehe jedoch Update 2 unten) . Aber das geht nicht, weil die eine oder andere Seite derFULL OUTER JOIN
fehlen könnte und das eine oder andere Feld möglicherweise NULL ist.
Kann ich das ersetzen, indem ich das ersetze? COALESCE(…)
durch etwas anderes , das in die Unterabfrage umgeschrieben wird?
Weitere Hinweise:
- Ich habe bereits einige Indizes hinzugefügt, um Leistungsprobleme mit dem Rest der Abfrage zu beheben - ohne die
COALESCE
diese ist es sehr schnell. - Zu meiner Überraschung werden beim Betrachten des Ausführungsplans keine Flags ausgelöst, selbst wenn die umschließende Unterabfrage und
WHERE
Anweisung eingeschlossen sind. Meine gesamten Unterabfragekosten im Analysator betragen0.0065736
. Hmph. Die Ausführung dauert vier Sekunden. - Das Ändern der Anwendung auf eine andere Abfrage
(z. B. Zurückgebenfunktioniert möglicherweise, aber als letzter Ausweg hoffe ich wirklich, dass ich die Ansicht optimieren kann, ohne die Anwendung berühren zu müssen.pe.StaffName AS PEStaffName, se.StaffName AS SEStaffName
und AusführenWHERE PEStaffName = 'X' OR SEStaffName = 'X'
) - Eine gespeicherte Prozedur wäre wahrscheinlich sinnvoller, aber die Anwendung wurde mit Entity Framework erstellt, und ich konnte nicht herausfinden, wie sie mit einem SP, der einen Tabellentyp zurückgibt, gut funktioniert (ein ganz anderes Thema).
Indizes
Die Indizes, die ich bisher hinzugefügt habe, sehen ungefähr so aus:
CREATE NONCLUSTERED INDEX [IX_PE_EventTime]
ON [dbo].[PE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[ThisThing])
CREATE NONCLUSTERED INDEX [IX_SE_EventTime]
ON [dbo].[SE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[OtherThing])
Aktualisieren
Hmm ... Ich habe versucht, die betroffene Veränderung oben zu simulieren, und es hat nicht geholfen. Dh vorher habe ) Z
ich hinzugefügtAND (pe.StaffName = 'SMITH, JOHN Q' OR se.StaffName = 'SMITH, JOHN Q')
, aber die Leistung ist die gleiche. Jetzt weiß ich wirklich nicht, wo ich anfangen soll.
Update 2
Durch den Kommentar von @ypercube zur Notwendigkeit des vollständigen Joins wurde mir klar, dass meine synthetisierte Abfrage eine wahrscheinlich wichtige Komponente ausgelassen hat. Während, ja, ich den vollständigen Join benötige, würde der Test, den ich oben durchgeführt habe, indem ich den gelöscht COALESCE
und nur eine Seite des Joins auf einen Wert ungleich Null getestet habe , die andere Seite des vollständigen Joins irrelevant machen , und der Optimierer hat dies wahrscheinlich verwendet Tatsache, um die Abfrage zu beschleunigen. Außerdem habe ich das Beispiel aktualisiert, um zu zeigen, dass StaffName
es sich tatsächlich um einen der Join-Schlüssel handelt - was wahrscheinlich einen erheblichen Einfluss auf die Frage hat. Ich neige jetzt auch zu seinem Vorschlag, dass es die Antwort sein könnte, dies in eine Drei-Wege-Union zu zerlegen, anstatt sich vollständig anzuschließen, und die Fülle von COALESCE
s, die ich sowieso mache, zu vereinfachen . Ich versuche es jetzt.
KeyField
, beide indiziert INCLUDE
das StaffName
Feld und mehrere andere Felder. Ich kann die Indexdefinitionen in der Frage posten. Ich arbeite auf einem Testserver daran, damit ich alle Indizes hinzufügen kann, die Sie für hilfreich halten, um es zu versuchen!
WHERE pe.ThisThing = 1 AND se.OtherThing = 0
Bedingung, dass der FULL OUTER
Join abgebrochen wird und die Abfrage einem inneren Join entspricht. Sind Sie sicher, dass Sie einen vollständigen Join benötigen?
INNER JOIN
, LEFT JOIN
mit WHERE IS NULL
Scheck, RIGHT mit IS NULL JOIN) und dann UNION ALL
die drei Teile. Auf diese Weise ist keine Verwendung erforderlich, COALESCE()
und es kann (möglicherweise) dem Optimierer helfen, das Umschreiben herauszufinden.