it-swarm-eu.dev

Co přesně znamená „No Join Predicate“ na serveru SQL?

MSDN " Chybí třída událostí předpovídání spojení " říká, že " označuje, že se provádí dotaz, který nemá predikát spojení".

Bohužel se to však nezdá tak snadné.

Například velmi jednoduchá situace:

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

V tabulkách nejsou žádná data, není zde žádné varování, i když zjevně nemá predikát spojení.

Pokud se podívám na dokumentaci k serveru SQL Server 2005 (stejný odkaz, pouze jiná verze serveru), je zde ještě jedna věta: " Tato událost je vytvořena, pouze pokud obě strany spojení vrátí více než jeden řádek. "To by v předchozí situaci mělo smysl. Nejsou žádná data, takže obě strany vrátí 0 řádků a žádné varování. Vložte řádky, dostanete varování. OK v pohodě.

Ale pro další matoucí situaci vložím do obou tabulek stejné hodnoty:

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

A já dostanu:

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

Proč je to tak?

Poznámka : Některé skripty, které jsem použil k detekci těchto chybných dotazů na mém serveru.

  1. samozřejmě prováděcí plán procedur
  2. použil výchozí trasování serveru k nalezení varování

    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. mezipaměť prováděcího plánu, k nalezení těchto plánů s upozorněními (jako je tato)

    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

Vaše otázka je podobná otázce tato . SQL Server může někdy odstranit predikát spojení z původního dotazu.

V případě, že uvidíte varování predikátu spojení, SQL Server v době kompilace zjistí, že tabulka konstant má pouze jednu odlišnou hodnotu a tato hodnota je 1, Takže přepíše dotaz jako

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

Na stole je predikát #temp Takto [tempdb].[dbo].[#temp1].[i] =(1)

Predikát spojení on t1.i = t2.i Nelze tímto způsobem odstranit při kompilaci při použití dvou tabulek nebo pokud tabulka konstant obsahuje více než jednu odlišnou hodnotu.


Více informací o tom naleznete v sérii Paul White 's Query Optimizer Deep Dive .

18
Martin Smith