2017-10-06 1 views
0

J'ai une requête qui joint une table en utilisant cette condition:ISNULL vs « IS NULL et champ = 0 »

(Apps.mfrId = Manufacturer.id OR Apps.mfrId IS NULL OR Apps.mfrId = 0) 

La requête prend 17 secondes pour exécuter, et en utilisant un profileur la requête provoque entre 5 - 30 messages (il varie chaque exécution) avec l'erreur "Erreur: 1222, Gravité: 16, Etat: 18".

Je laisse la requête exactement la même chose, mais je changer la condition ci-dessus pour lire:

(Apps.mfrId = Manufacturer.id OR ISNULL(apps.mfrId, 0) = 0) 

... et maintenant la même requête avec celui-ci fonctionne dans le changement 140ms et n'a pas d'erreurs de verrouillage.

Pourquoi cela est-il possible? Remarque: avant de tester sur les tables Fabricant et Apps, j'ai exécuté DBCC CHECKTABLE avec l'option repair_bebuild et j'ai également reconstruit les index sur les deux tables.

Notez également qu'il n'y a pas d'autres requêtes exécutées sur la base de données en même temps.

Voici une version simplifiée de la requête qui contient des erreurs:

select top 2000 Apps.object_id 
from 
    Manufacturer 
    INNER JOIN Apps ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0 
    ) 
where 
    Apps.OBJECT_ID = 6879149 

Si au lieu de « top 2000 » J'utilise « top 1000 » la requête est terminée en un peu plus de 100 ms.

+0

Avez-vous montré le plan d'exécution pour chaque requête? –

+0

cette condition de jointure semble terriblement étrange. Est-ce une jointure gauche, une jointure interne, ???? Je suis intéressé par pourquoi vous voudriez une condition de jointure qui serait surchargée si le apps.mfrid était nul. On dirait que vous voulez simplement une jointure complète. Quel est l'objectif final? – scsimon

+0

@scsimon C'est une jointure interne. Je travaille avec des tables que je n'ai pas conçues. Apps.mfrId est une clé étrangère nullable à Manufacturer.id (sans configuration de contraintes de clé étrangère). Pour une raison quelconque, la conception d'origine autorise NULL ou 0, et NULL ou 0 ont la même signification (aucune valeur valide n'est définie). –

Répondre

0

Si Apps.mfrId est nul, vous rejoignez tous les fabricants.

De vos tests, "OU ISNULL (apps.mfrId, 0) = 0" le fait moins cher que "OU Apps.mfrId IS NULL OU Apps.mfrId = 0"

Ces paraient assez équivalent, et vous avez déjà une solution, alors la question est de savoir pourquoi et/ou comment faire fonctionner le non-performer.

En ce qui concerne ces performances, si tout ce que vous dites est exact, comme nous commençons à demander pourquoi, nous commençons à pointer vers le Optimiseur de requête. S'il fonctionne différemment pour les mêmes paramètres, il aura un plan de requête différent. Vous verrez probablement une analyse de table au lieu de l'utilisation de l'index, ou une autre explication à la mauvaise performance.

Je vous encourage à comparer les plans de requête, ou d'amener quelqu'un d'autre à, pour aider à apporter une réponse à ce qu'il fait différemment. Mais, vous pouvez déjà dire qu'il fait quelque chose différemment.

Une possibilité est que lorsque Optimiseur de requête fixe sont libérés, ils par défaut ne sont pas activés, sauf si drapeau 4199 (pour tous les correctifs ou d'autres indicateurs pour des corrections spécifiques) sont activés. En effet, une correction générale peut être bonne pour la plupart, mais peut également casser une application qui a été optimisée dans l'environnement d'exister les problèmes antérieurs.

https://dba.stackexchange.com/questions/102292/trace-flag-4199-enable-globally

Ne tournant 4199 sur l'aide?

select top 2000 Apps.object_id 
from 
    Manufacturer 
    INNER JOIN Apps ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0 
    ) 
where 
    Apps.OBJECT_ID = 6879149 
OPTION(QUERYTRACEON 4199) 

Un autre sujet lié est paramètre renifler. Parfois, un plan de requête peut être mis en cache qui est optimal pour un paramètre mais horrible pour un autre paramètre.Dans votre cas, une application peut renvoyer un fabricant, mais une autre application peut renvoyer tous les fabricants, ce qui explique pourquoi cela vaut la peine d'être mentionné. Cela se manifeste généralement par le fait que le même code fonctionne de manière incohérente lorsque les paramètres sont différents. Vous pouvez essayer de désactiver le reniflage des paramètres ou de forcer la recompilation pour aider à diagnostiquer si cela semble faire partie du problème.

What are the main differences between OPTION(OPTIMIZE FOR UNKNOWN) and OPTION(RECOMPILE)?

J'ai vu des situations où se sentaient comme l'optimisateur de requêtes avaient abandonné sur les indices lisant attentivement, juste parce que la déclaration est devenu trop complexe. Mais cela ne semble pas probable dans votre exemple; Les bogues de l'optimiseur de requêtes sont corrigés, mais pour les utiliser, vous devez toujours activer les indicateurs de requête, sinon le correctif que vous installez peut très bien ne rien faire. Parfois, vous pouvez également essayer de guider l'optimiseur sql. S'il existe un index qui devrait toujours être utilisé, cela peut être donné comme un indice de requête.

Je serais également curieux de savoir si ce qui suit supprime la question:

select top 2000 Apps.object_id 
from 
(
    select Apps.object_id, Apps.mfrId 
    from Apps 
    where Apps.OBJECT_ID = 6879149 
) Apps 
left join Manufacturer ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0) 

Si tous les correctifs de l'optimiseur de requêtes sont activés, et le problème persiste, il faut se demander pourquoi l'optimiseur de requêtes a ce. Il ne peut utiliser que des index existants et il peut seulement déterminer si l'utilisation d'un index sera bénéfique si des statistiques existent. Pendant ce temps, des statistiques obsolètes conduiront à de mauvais choix. Il peut être bon de reconstruire/réorganiser les index et mettre à jour les statistiques périodiquement. Vous pouvez essayer de le faire et voir si cela a un effet.

Conclusion

Puisque vous avez déjà une solution de travail, ne l'utiliser. Mais votre question est de savoir pourquoi deux choses très similaires obtiennent des résultats très différents. En supposant que le même paramètre est utilisé, et en tenant compte du fait que vos deux options sont si similaires, le problème pointe vers un mauvais choix de l'optimiseur de requête. Cela suggère que vous devrez peut-être à la fois installer les derniers correctifs (s'ils ne sont pas installés) et activer 4199 pour activer tous les correctifs de l'optimiseur de requêtes que vous avez déjà installés via les correctifs de serveur SQL. Cela inclut l'ajout d'OPTION (QUERYTRACEON 4199) au bas de votre SQL ou l'activation de 4199 globalement ou similaire.

+0

L'idée est la suivante. Nous avons une application (une application étant où une partie est utilisée sur un véhicule). Si l'application est définie par le fabricant, ce véhicule est associé à ce fabricant.Si le véhicule n'a pas de fabricant défini (son mfrid est 0 ou nul), il est associé à tous les fabricants. –

+0

Oui, mais pourquoi la variation ISNULL est-elle des centaines de fois plus rapide, et pourquoi ne pas obtenir des tonnes d'erreur 1222 lorsque l'autre version de la requête obtient 5 - 30 1222 erreurs. –

+0

Ya, je l'ai exécuté via l'optimiseur, et utilisé l'option de comparaison dans le studio de gestion SQL pour comparer les plans d'exécution. J'obtiens le concept de ce que ça me montre, mais ce n'est pas très utile pour moi dans ce cas. –