Проблема CROSS APPLY - фильтрация отношений «многие к одному» - PullRequest
4 голосов
/ 05 октября 2011

Я обнаружил проблему с обработкой MS SQL Server CROSS APPLY.

База данных, с которой я работаю, имеет систему ценообразования со следующей схемой:

Сервис -> Модель цены <- Компонент цены (<i> '->' обозначаетВнешний ключ, указывающий на таблицу )

Некоторые ценовые модели имеют "пошаговое ценообразование", что означает, что когда параметр количества достигает различных порогов, цена будет увеличиваться (1-3 единицы - это цена А, 4-8 единиц - цена B и т. Д.).

Проблема, с которой я столкнулся, заключается в том, что INNER JOIN между [Service] и [Price Component] в [Price Model ID] создает дублирующиеся строки, поскольку я на самом деле не использую цены в PriceКомпонент, просто еще одно поле в таблице, одинаковое для каждой из строк [Компонент цены].

SELECT * 
  FROM [Service] s
 INNER JOIN [Price Component] pc
    ON s.[Price Model Id] = pc.[Price Model Id]

Логическим решением этой проблемы является замена INNER JOIN на CROSS APPLY, который делает это:

SELECT * 
  FROM [Service] s
 CROSS APPLY (SELECT TOP 1 * 
                FROM [Price Component] pc
               WHERE s.[Price Model Id] = pc.[Price Model Id]
             ) AS pc

Проблема заключается в том, что эффективность полностью разрушается внекоторые другие соединения, казалось бы, не связаны с этим изменением.Если посмотреть на план выполнения, объединение, которое раньше занимало 2,3 цикла, теперь занимает 4,8 миллиона циклов.

Я попытался добавить DISTINCT к исходному запросу (поскольку он не использует уникальные данные из таблицы [Компонент цены], и это функциональное решение, за исключением того, что он в четыре раза увеличивает время выполнения. IЯ также пытался вернуть только необходимое мне значение из таблицы [Price Component], но, похоже, это не очень помогает:

SELECT * 
  FROM [Service] s
 CROSS APPLY (SELECT DISTINCT pc.moneyUnitId 
                FROM [Price Component] pc
               WHERE s.[Price Model Id] = pc.[Price Model Id] 
             ) AS pc

Странно, но изменение CROSS APPLY на OUTER APPLY исправляетпроблемы с другими объединениями, но не позволяют использовать CROSS APPLY (как я понимаю, в основном это разница между INNER JOIN и OUTER JOIN).

Есть ли у кого-нибудь мысли илипонимание того, что может вызвать безумное увеличение сложности с CROSS APPLY?

ОБНОВЛЕНИЕ

Итак, после прочтения еще немного о том, как интерпретировать план выполнения, яВы узнали следующее:

  • Оригинальный запрос (с использованием INNER JOIN s) - это длинная серия вложенных циклов, которая начинается с любых данных фильтра, которые вы ей предоставляете.Пока фильтр работает с индексированными полями.

  • Модифицированный запрос (с использованием CROSS APPLY) представляет собой более длинный ряд хэш-соответствий и объединяет все предоставленные вами таблицы, кроме таблиц с фильтрами, затемприменяет фильтры в последнюю очередь.Всегда медленнее смерти.

  • Рабочий модифицированный запрос (с OUTER APPLY), делает то же самое, что и оригинал, но не исключает результаты, которые не совпадают с WHEREпункт.Так же быстро, как и оригинал.

Итак, проблема в том, почему CROSS APPLY меняет план, чтобы объединить все таблицы перед запрошенным фильтром?

Ответы [ 3 ]

1 голос
/ 14 ноября 2012

Почему бы и нет:

select t1.colA, t3.colX from table1 t1
inner join (select distinct t2.t1FK, t2.colX from table2 t2) t3 on t1.ID = t3.t1FK
0 голосов
/ 05 октября 2011

Если вы используете только одно поле из таблицы с одним-многими, вы рассматривали возможность использования group by и max () для фильтрации записей?

select a.field1, a.field2, max(b.field3)
from table1 a
join table2 b on a.someid = b.someid
group by a.field1, a.field2
0 голосов
/ 05 октября 2011

Если планы выполнения не совпадают между JOIN и CROSS APPLY, тогда оптимизатор запросов использует логический оператор Nested Loops, который в некоторых случаях может отрицательно повлиять на производительность. Здесь вы найдете основную информацию о вложенных циклах: http://msdn.microsoft.com/en-us/library/ms191318(v=sql.90).aspx

...