MSDN " Classe d'événement de prédicat de jointure manquante " dit " indique qu'une requête est en cours d'exécution qui n'a pas de prédicat de jointure".
Mais malheureusement, cela ne semble pas être aussi simple que cela.
Par exemple, situation très simple:
create table #temp1(i int);
create table #temp2(i int);
Select * from #temp1, #temp2 option (recompile);
Il n'y a pas de données dans les tableaux, il n'y a pas d'avertissement non plus, bien qu'il n'y ait évidemment aucun prédicat de jointure.
Si je regarde la documentation de SQL Server 2005 (le même lien, juste une autre version de serveur), il y a une phrase supplémentaire: " Cet événement est produit uniquement si les deux côtés de la jointure renvoient plus d'une ligne. "Cela serait parfaitement logique dans la situation précédente. Il n'y a pas de données, les deux côtés renvoient donc 0 lignes et aucun avertissement. Insérez des lignes, recevez un avertissement. OK cool.
Mais pour la prochaine situation déroutante, j'insère les mêmes valeurs dans les deux tableaux:
Insert into #temp1 (i) values (1)
Insert into #temp1 (i) values (1)
Insert into #temp2 (i) values (1)
Insert into #temp2 (i) values (1)
Et je reçois:
-- no warning:
Select * from #temp1 t1
inner join #temp2 t2 on t1.i = t2.i
option (recompile)
-- has warning:
Select * from #temp1 t1
inner join (select 1 i union all select 1) t2 on t1.i = t2.i
option (recompile)
Pourquoi en est-il ainsi?
Remarque : certains scripts que j'ai utilisés pour détecter ces mauvaises requêtes sur mon serveur.
utilisé la trace du serveur par défaut pour rechercher les avertissements
Declare @trace nvarchar(500);
Select @trace = cast(value as nvarchar(500))
From sys.fn_trace_getinfo(Null)
Where traceid = 1 and property = 2;
Select t.StartTime, te.name, *
From sys.fn_trace_gettable(@trace, 1) t
Inner join sys.trace_events te on t.EventClass = te.trace_event_id
where EventClass = 80
order by t.StartTime desc
cache du plan d'exécution, pour trouver ces plans avec des avertissements (comme celui-ci)
WITH XMLNAMESPACES (default 'http://schemas.Microsoft.com/sqlserver/2004/07/showplan')
SELECT
Cast('<?SQL ' + st.text + ' ?>' as xml) sql_text,
pl.query_plan,
ps.execution_count,
ps.last_execution_time,
ps.last_elapsed_time,
ps.last_logical_reads,
ps.last_logical_writes
FROM sys.dm_exec_query_stats ps with (NOLOCK)
Cross Apply sys.dm_exec_sql_text(ps.sql_handle) st
Cross Apply sys.dm_exec_query_plan(ps.plan_handle) pl
WHERE pl.query_plan.value('(//Warnings/@NoJoinPredicate)[1]', 'bit') = 1
Order By last_execution_time desc
OPTION (RECOMPILE);
Votre question est similaire à celle-ci . Parfois, SQL Server peut supprimer un prédicat de jointure de la requête d'origine.
Dans le cas où vous voyez un avertissement de prédicat de jointure, SQL Server détecte au moment de la compilation que la table des constantes n'a qu'une seule valeur distincte et que cette valeur est 1
, Donc réécrit la requête sous la forme
SELECT *
FROM (SELECT *
FROM #temp1 t1
WHERE t1.i = 1) t1
CROSS JOIN (SELECT 1 i
UNION ALL
SELECT 1) t2
Il y a un prédicat sur le scan de table de #temp
Comme suit [tempdb].[dbo].[#temp1].[i] =(1)
Le prédicat de jointure de on t1.i = t2.i
Ne peut pas être supprimé de cette manière au moment de la compilation lorsque vous utilisez deux tables ou si la table de constantes contient plusieurs valeurs distinctes.
Vous trouverez plus d'informations à ce sujet dans la série Paul WhiteQuery Optimizer Deep Dive .