Apa sebenarnya arti dari "No Join Predicate" di SQL Server?

23

MSDN " Missing Join Predicate Class Class " mengatakannya " menunjukkan bahwa kueri sedang dieksekusi yang tidak memiliki predikat gabung ".

Namun sayangnya sepertinya tidak semudah itu.

Misalnya, situasi yang sangat sederhana:

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

Tidak ada data dalam tabel, tidak ada peringatan juga, meskipun jelas tidak memiliki predikat bergabung.

Jika saya melihat dokumentasi untuk SQL Server 2005 (tautan yang sama, hanya versi server lain), ada kalimat tambahan: " Acara ini diproduksi hanya jika kedua sisi gabungan mengembalikan lebih dari satu baris. " Ini akan membuat akal yang sempurna dalam situasi sebelumnya. Tidak ada data, sehingga kedua belah pihak mengembalikan 0 baris dan tidak ada peringatan. Masukkan baris, dapatkan peringatan. OK keren.

Tetapi untuk situasi membingungkan berikutnya, saya memasukkan nilai yang sama di kedua tabel:

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

Dan saya mendapatkan:

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

Kenapa begitu?

Catatan : beberapa skrip yang saya gunakan untuk mendeteksi kueri buruk ini di server saya.

  1. tentu saja, rencana pelaksanaan prosedur
  2. menggunakan jejak server default untuk menemukan peringatan

    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 rencana pelaksanaan, untuk menemukan rencana itu dengan peringatan (seperti ini)

    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
sumber

Jawaban:

17

Pertanyaan Anda mirip dengan yang ini . Terkadang SQL Server dapat menghapus predikat gabungan dari kueri asli.

Dalam kasus di mana Anda melihat peringatan predikat bergabung SQL Server mendeteksi pada waktu kompilasi bahwa tabel konstanta hanya memiliki satu nilai yang berbeda dan nilai itu 1begitu menulis ulang kueri sebagai

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

Ada predikat pada pemindaian tabel #tempsebagai berikut[tempdb].[dbo].[#temp1].[i] =(1)

Predikat gabungan dari on t1.i = t2.itidak dapat dihapus dengan cara ini pada waktu kompilasi ketika menggunakan dua tabel atau jika tabel konstanta berisi lebih dari satu nilai yang berbeda.


Info lebih lanjut tentang ini dapat ditemukan dalam Paul Putih 's Query Optimizer Deep Dive seri.

Martin Smith
sumber