Was genau bedeutet "No Join Predicate" in SQL Server?

23

MSDN " Fehlende Join-Prädikat-Ereignisklasse " gibt an, dass "eine Abfrage ausgeführt wird, die kein Join-Prädikat enthält ".

Aber leider scheint es nicht so einfach zu sein.

Zum Beispiel sehr einfache Situation:

create table #temp1(i int);
create table #temp2(i int);
Select * from #temp1, #temp2 option (recompile);

Die Tabellen enthalten keine Daten, und es wird auch keine Warnung ausgegeben, obwohl offensichtlich kein Join-Prädikat vorhanden ist.

Wenn ich mir die Dokumentation für SQL Server 2005 anschaue (derselbe Link, nur eine andere Serverversion), erscheint ein zusätzlicher Satz: " Dieses Ereignis wird nur erzeugt, wenn beide Seiten des Joins mehr als eine Zeile zurückgeben. " perfekter Sinn in der vorherigen Situation. Da keine Daten vorhanden sind, geben beide Seiten 0 Zeilen und keine Warnung zurück. Zeilen einfügen, Warnung erhalten. OK Cool.

Aber für die nächste verwirrende Situation füge ich die gleichen Werte in beide Tabellen ein:

Insert into #temp1 (i) values (1)
Insert into #temp1 (i) values (1)
Insert into #temp2 (i) values (1)
Insert into #temp2 (i) values (1)

Und ich bekomme:

-- 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)

Warum ist das so?

Hinweis : Einige Skripte, die ich zum Erkennen dieser fehlerhaften Abfragen auf meinem Server verwendet habe.

  1. natürlich Ausführungsplan von Verfahren
  2. Standard-Server-Trace verwendet, um Warnungen zu finden

    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. Ausführungsplan-Cache, um diese Pläne mit Warnungen zu finden (wie diese hier)

    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);
Jānis
quelle

Antworten:

17

Ihre Frage ähnelt dieser . Manchmal kann SQL Server ein Join-Prädikat aus der ursprünglichen Abfrage entfernen.

In dem Fall, in dem eine Join-Prädikat-Warnung angezeigt wird, stellt SQL Server beim Kompilieren fest, dass die Konstantentabelle nur einen eindeutigen Wert hat und dieser Wert 1die Abfrage so umschreibt, dass

SELECT *
FROM   (SELECT *
        FROM   #temp1 t1
        WHERE  t1.i = 1) t1
       CROSS JOIN (SELECT 1 i
                   UNION ALL
                   SELECT 1) t2 

Es gibt ein Prädikat für den Tabellenscan #tempwie folgt[tempdb].[dbo].[#temp1].[i] =(1)

Das Join-Prädikat von on t1.i = t2.ikann beim Kompilieren nicht auf diese Weise entfernt werden, wenn zwei Tabellen verwendet werden oder wenn die Konstantentabelle mehr als einen eindeutigen Wert enthält.


Mehr Informationen zu dieser findet sich in Paul White ‚s Abfrageoptimierer Deep Dive - Serie.

Martin Smith
quelle