Почему ссылка на переменную в предикате объединения заставляет вложенные циклы?

16

Я недавно сталкивался с этой проблемой и не мог найти никакого обсуждения этого онлайн.

Запрос ниже

DECLARE @S VARCHAR(1) = '';

WITH T
     AS (SELECT name + @S AS name2,
                *
         FROM   master..spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2;

Всегда получает план вложенных циклов

введите описание изображения здесь

Попытка вызвать проблему с INNER HASH JOIN или INNER MERGE JOINподсказок приводит к следующей ошибке.

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

Я нашел обходной путь, который позволяет использовать хеш или объединения слиянием - оборачивая переменную в агрегат. Сгенерированный план значительно дешевле (19,2025 против 0,261987)

DECLARE @S2 VARCHAR(1) = '';

WITH T
     AS (SELECT name + (SELECT MAX(@S2)) AS name2,
                *
         FROM   spt_values)
SELECT *
FROM   T T1
       INNER JOIN T T2
         ON T1.name2 = T2.name2; 

введите описание изображения здесь

В чем причина такого поведения? и есть ли лучший обходной путь, чем тот, который я нашел? (что, возможно, не требует дополнительных ветвей плана выполнения)

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

Ответы:

13

Я пробовал ваш запрос на экземпляре SQL 2012, и флаг трассировки 4199, кажется, решает проблему. С его включением я получаю объединение слиянием общей стоимостью 0,24 и ни одной дополнительной ветки.

Специальная статья по этой проблеме: проблемы с производительностью, возникающие, когда предикат соединения в вашем запросе имеет внешние ссылочные столбцы в SQL Server 2005 или SQL Server 2008

введите описание изображения здесь

Для дальнейшей квалификации TF 4199 включает все исправления оптимизатора. Видеть эту ссылку для получения дополнительной информации. Включение всего сразу может иметь странные побочные эффекты, поэтому, если вы можете найти конкретное исправление, может быть лучше включить исправление самостоятельно.

Вы можете включить флаг трассировки для каждого запроса, используя OPTION (QUERYTRACEON 4199);

Том V - Команда Моника
источник
0

Старый вопрос, но, увидев ответ, не было супер определенным, я подумал, что опубликую найденный мной обходной путь. Не уверен, почему оптимизатор запросов не работает HASH, но я думаю, что это не нравится, MERGEпотому что у него нет отсортированного ввода. 2012/14,

DECLARE @S VARCHAR(1) = '';

    WITH T
        AS (SELECT TOP (2147483647)
                name + @S AS name2,
                *
            FROM   master..spt_values
            ORDER BY name + @S)
    SELECT *
    FROM   T T1
           INNER JOIN T T2
             ON T1.name2 = T2.name2;

производит следующий план:

введите описание изображения здесь

Форсирование TOPи ORDER BYin the cte, кажется, дают оптимизатору достаточно знаний о наборе данных для выполнения MERGE JOIN.

VBlades
источник