web-dev-qa-db-fra.com

Que signifie exactement "No Join Predicate" dans SQL Server?

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.

  1. bien sûr, plan d'exécution des procédures
  2. 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
    
  3. 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);
    
23
Jānis

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 .

18
Martin Smith