Что именно означает «Предикат без присоединения» в SQL Server?

23

MSDN « Отсутствует класс события предиката соединения » говорит, что « указывает на то, что выполняется запрос без предиката соединения ».

Но, к сожалению, все не так просто.

Например, очень простая ситуация:

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

В таблицах нет данных, также нет предупреждений, хотя, очевидно, у них нет предиката соединения.

Если бы я взглянуть на документацию по 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)

И я получаю:

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

Почему это так?

Примечание : некоторые скрипты, которые я использовал для обнаружения этих плохих запросов на моем сервере.

  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
  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);
Яниса
источник

Ответы:

17

Ваш вопрос похож на этот . Иногда 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 

Существует предикат на столе сканирования #tempследующим[tempdb].[dbo].[#temp1].[i] =(1)

Таким образом, предикат соединения on t1.i = t2.iне может быть удален во время компиляции при использовании двух таблиц или если таблица констант содержит более одного отдельного значения.


Более подробную информацию об этом можно найти в серии статей Deep White 's Query Optimizer Deep Dive .

Мартин Смит
источник