查询中的表达式在什么时候应用于WHERE视图,在生成视图的记录集时,或者应用于已经生成的集合,它是否取决于任何因素?
有两种特殊情况感兴趣:
1.在视图主体中的简单请求的情况下
2.在形成视图的请求使用窗口的情况下
示例 1:
CREATE VIEW dbo.vw_StockRemnants
AS
SELECT tt.PKID AS ИД,
tt.Name AS Наименование,
tt.priceIn AS Вход,
tt.Price AS Цена
FROM Item AS tt
WHERE tt.iditem IS NOT NULL;
SELECT *
FROM vw_StockRemnants
WHERE dbo.vw_StockRemnants.[ИД] = 12345;
示例 2:
CREATE VIEW dbo.vw_StockRemnants
AS
WITH CTE([ИД],
[Наименование],
[Вход],
[Цена],
rnumber,
rcount)
AS (SELECT tt.PKID AS ИД,
tt.Name AS Наименование,
tt.priceIn AS Вход,
tt.Price AS Цена,
ROW_NUMBER() OVER(PARTITION BY tt.pkid ORDER BY tt.sn) AS rnumber,
COUNT(tt.sn) OVER(PARTITION BY tt.pkid) AS rcount
FROM Item AS tt
WHERE tt.iditem IS NOT NULL)
SELECT *
FROM CTE AS c
WHERE c.rcount = 1;
SELECT *
FROM vw_StockRemnants
WHERE dbo.vw_StockRemnants.[ИД] = 12345;
当使用视图执行查询时(也使用内联表值函数),优化器会将视图定义替换到查询中并尝试优化生成的复合查询。
В первом случае запрос скорее всего выполнится как
А во втором, по-видимому, как
т.к. во внутреннем запросе нумерация и подсчёт количества происходят в группах, разбитых по
tt.PKID, а предикатtt.PKID = 12345, так же как иc.rcount = 1, делает фильтрацию группы.При построении плана запроса оптимизатор старается применять предикаты настолько рано, насколько это возможно (т.н. predicate pushdown или filter pushdown), т.к., во-первых, выгодно на ранних этапах плана отсечь ненужные данные (меньше работы на следующих этапах), и, во-вторых, раннее применение предиката положительно сказывается на возможности использования индексов.
Рассмотрим пример. Пусть есть таблица
Выполним запрос с подзапросом
и посмотрим план выполнения (графический и текстовый)
Как видим, оптимизатор объединил оба условия и поместил их в узел Table Scan (чтение данных таблицы).
Если бы запрос исполнялся именно так как он записан, то мы бы увидели
т.е. чтение таблицы с условием
[B] IS NOT NULLкак первый этап плана, и фильтрацию набора строк с условием[A]=(1)как второй его этап. В данном случае вряд ли это заметно понизило бы производительность. Если бы, однако, на столбце[A]у нас был индекс, то такой план не позволил бы его использовать оптимально (или даже вовсе).Однако для применения predicate pushdown у оптимизатора должно быть достаточно оснований. Препятствовать этому виду оптимизации может использование в предикатах недетерминированных выражений (в т.ч. с участием пользовательских скалярных функций). Также ни один вид оптимизации не должен нарушать семантику запроса (корректность результата превыше скорости).
Смоделируем второй случай. Пусть есть таблица с данными
Мы делаем запрос
семантика которого такова:
ACOUNT(C) = 1A = 1План выполнения запроса
показывает, однако, что условие
A = 1, заданное на последнем этапе, оптимизатор перенёс на первый этап, объединив его с[B] IS NOT NULLв узле Table Scan.Если изменить запрос, убрав
PARTITION BY, напримерто это меняет семантику запроса так, что оптимизатор уже не может применить условие
[A] = 1при чтении данных из таблицы, и в плане запросамы видим в свойствах Table Scan лишь условие
[B] IS NOT NULL, а условие[A]=(1)применяется в Filter на самом последнем этапе.