J'ai une vue large que j'utilise depuis une application. Je pense que j'ai réduit mon problème de performances, mais je ne sais pas comment le résoudre. Une version simplifiée de la vue ressemble à ceci:
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
Cela ne justifie probablement pas toute la raison de la structure de la requête, mais peut-être vous donne une idée - cette vue rejoint deux tables très mal conçues sur lesquelles je n'ai pas de contrôle et essaie de synthétiser certaines informations.
Donc, puisque c'est une vue utilisée depuis l'application, en essayant d'optimiser je l'enveloppe dans un autre SELECT, comme ceci:
SELECT * FROM (
-- … above code …
) Q
WHERE StaffName = 'SMITH, JOHN Q'
car l'application recherche des membres du personnel spécifiques dans le résultat.
Le problème semble être la COALESCE(pe.StaffName, se.StaffName) AS StaffName
section et que je sélectionne dans la vue StaffName
. Si je change cela en pe.StaffName AS StaffName
ou se.StaffName AS StaffName
, les problèmes de performances disparaissent (mais voir la mise à jour 2 ci-dessous) . Mais cela ne fonctionnera pas car un côté ou l'autre du FULL OUTER JOIN
peut être manquant, donc l'un ou l'autre champ peut être NULL.
Puis-je refactoriser ceci en remplaçant le COALESCE(…)
par quelque chose d'autre, qui sera réécrit dans la sous-requête?
Autres notes:
- J'ai déjà ajouté quelques index pour résoudre les problèmes de performances avec le reste de la requête - sans
COALESCE
cela, c'est très rapide. - À ma grande surprise, le fait de regarder le plan d'exécution ne déclenche aucun indicateur, même lorsque la sous-requête et la
WHERE
déclaration d' habillage sont incluses. Mon coût total de sous-requête dans l'analyseur est0.0065736
. Hmph. L'exécution prend quatre secondes. - Changer l'application pour interroger différemment
(par exemple retournerpourrait fonctionner, mais en dernier recours - j'espère vraiment pouvoir optimiser la vue sans avoir à recourir à toucher l'application.pe.StaffName AS PEStaffName, se.StaffName AS SEStaffName
et faireWHERE PEStaffName = 'X' OR SEStaffName = 'X'
) - Une procédure stockée aurait probablement plus de sens pour cela, mais l'application est construite avec Entity Framework, et je n'ai pas pu comprendre comment la faire fonctionner correctement avec un SP qui renvoie un type de table (un autre sujet entièrement).
Index
Les index que j'ai ajoutés jusqu'à présent ressemblent à ceci:
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])
Mise à jour
Hmm… J'ai essayé de simuler le changement frappé ci-dessus, et cela n'a pas aidé. C'est-à-dire, avant ) Z
ci-dessus, j'ai ajouté AND (pe.StaffName = 'SMITH, JOHN Q' OR se.StaffName = 'SMITH, JOHN Q')
, mais les performances sont les mêmes. Maintenant, je ne sais vraiment pas par où commencer.
Update 2
Le commentaire de @ypercube sur la nécessité de la jointure complète m'a fait réaliser que ma requête synthétisée laissait de côté un composant probablement important. Alors que, oui, j'ai besoin de la jointure complète, le test que j'ai fait ci-dessus en supprimant COALESCE
et en testant un seul côté de la jointure pour une valeur non nulle rendrait l'autre côté de la jointure complète non pertinent , et l'optimiseur utilisait probablement cela fait pour accélérer la requête. De plus, j'ai mis à jour l'exemple pour montrer qu'il StaffName
s'agit en fait d'une des clés de jointure - ce qui a probablement une incidence significative sur la question. Je penche également maintenant vers sa suggestion selon laquelle la rupture en une union à trois au lieu d'une jointure complète peut être la réponse, et simplifiera l'abondance de COALESCE
s que je fais de toute façon. L'essayer maintenant.
KeyField
, à la fois indexe INCLUDE
le StaffName
champ et plusieurs autres champs. Je peux poster les définitions d'index dans la question. J'y travaille sur un serveur de test afin que je puisse ajouter tous les index que vous pensez être utiles à essayer!
WHERE pe.ThisThing = 1 AND se.OtherThing = 0
condition qui annule la FULL OUTER
jointure et rend la requête équivalente à une jointure interne. Êtes-vous sûr d'avoir besoin d'une adhésion COMPLÈTE?
INNER JOIN
, LEFT JOIN
avec WHERE IS NULL
vérification, RIGHT JOIN with IS NULL) puis UNION ALL
les trois parties. De cette façon, il ne sera pas nécessaire d'utiliser COALESCE()
et cela pourrait (pourrait juste) aider l'optimiseur à comprendre la réécriture.