Que signifie exactement «No Join Predicate» dans SQL Server?


23

MSDN " Missing Join Predicate Event Class " indique qu'il " 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 jette un œil à 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. " sens parfait 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 cela 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);

Réponses:


17

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 1donc réécrit la requête en

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 l'analyse de table #tempcomme suit[tempdb].[dbo].[#temp1].[i] =(1)

Le prédicat de jointure de on t1.i = t2.ine peut pas être supprimé de cette manière lors de la compilation lorsque vous utilisez deux tables ou si la table de constantes contient plusieurs valeurs distinctes.


En savoir plus sur ce qui peut être trouvé dans Paul White de Optimiseur de requête Deep Dive série.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.