SQL Server 中的“No Join Predicate”究竟是什么意思?

Jān*_*nis 24 sql-server-2005 sql-server-2008 t-sql

MSDN“缺少连接谓词事件类”表示“表示正在执行没有连接谓词的查询”。

但不幸的是,这似乎并不那么容易。

例如,非常简单的情况:

create table #temp1(i int);
create table #temp2(i int);
Select * from #temp1, #temp2 option (recompile);
Run Code Online (Sandbox Code Playgroud)

表中没有数据,也没有警告,尽管它显然没有连接谓词。

如果我查看 SQL Server 2005 的文档(相同的链接,只是其他服务器版本),会有一句额外的句子:“仅当连接的双方返回多于一行时才会产生此事件。 ”这将使在以前的情况下完美的感觉。没有数据,所以双方都返回0行,没有警告。插入行,得到警告。嗯不错。

但是对于下一个令人困惑的情况,我在两个表中插入相同的值:

Insert into #temp1 (i) values (1)
Insert into #temp1 (i) values (1)
Insert into #temp2 (i) values (1)
Insert into #temp2 (i) values (1)
Run Code Online (Sandbox Code Playgroud)

我得到:

-- 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)
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

注意:我用来在我的服务器上检测这些错误查询的一些脚本。

  1. 当然,程序的执行计划
  2. 使用默认服务器跟踪来查找警告

    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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 执行计划缓存,找到那些有警告的计划(比如这个)

    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);
    
    Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 18

你的问题和这个类似。有时 SQL Server 可以从原始查询中删除连接谓词。

如果您看到连接谓词警告,SQL Server 在编译时检测到常量表只有一个不同的值,并且该值1将查询重写为

SELECT *
FROM   (SELECT *
        FROM   #temp1 t1
        WHERE  t1.i = 1) t1
       CROSS JOIN (SELECT 1 i
                   UNION ALL
                   SELECT 1) t2 
Run Code Online (Sandbox Code Playgroud)

表扫描的谓词#temp如下[tempdb].[dbo].[#temp1].[i] =(1)

on t1.i = t2.i当使用两个表或常量表包含多个不同的值时,在编译时不能以这种方式删除的连接谓词。


更多关于这方面的信息可以在Paul WhiteQuery Optimizer Deep Dive系列中找到。

  • @ChrisSchaller:是和否。没有连接谓词的嵌套循环的效果可能非常糟糕,例如,如果它强制对唯一结果进行全表扫描。因此,如果您的查询运行缓慢并且这是一个警告,请查看修复它。 (2认同)