Почему нет оконных функций в предложениях where?
заголовок говорит Все, Почему я не могу использовать оконную функцию в предложении where в SQL Server?
этот запрос имеет смысл:
select id, sales_person_id, product_type, product_id, sale_amount
from Sales_Log
where 1 = row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc)
но это не работает. Есть ли лучший способ, чем CTE/Subquery?
редактировать
для чего это стоит, это запрос с CTE:
with Best_Sales as (
select id, sales_person_id, product_type, product_id, sale_amount, row_number() over (partition by sales_person_id, product_type, product_id order by sales_amount desc) rank
from Sales_log
)
select id, sales_person_id, product_type, product_id, sale_amount
from Best_Sales
where rank = 1
редактировать
+1 для ответов, показывающих с подзапросом, но на самом деле я ищу рассуждения за невозможностью использовать оконные функции в предложениях where.
7 ответов
почему я не могу использовать оконную функцию в предложении where в SQL Server?
один ответ, хотя и не особенно информативный, потому что спецификация говорит, что вы не можете.
см. статью Ицика Бен Гана -логическая обработка запросов: что это такое и что это значит для вас и в частности изображения. Оконные функции оцениваются во время SELECT
на результирующем наборе, оставшемся после WHERE
/JOIN
/GROUP BY
/HAVING
положения были рассмотрены (шаг 5.1).
на самом деле я ищу причины, по которым не могу использовать оконные функции в предложениях where.
причина, по которой они не допускаются в WHERE
предложение заключается в том, что это создаст двусмысленность. Кража примера Ицика Бен Гана из высокопроизводительный T-SQL с использованием оконных функций (стр. 25)
предположим, что ваш стол был
CREATE TABLE T1
(
col1 CHAR(1) PRIMARY KEY
)
INSERT INTO T1 VALUES('A'),('B'),('C'),('D'),('E'),('F')
запрос
SELECT col1
FROM T1
WHERE ROW_NUMBER() OVER (ORDER BY col1) <= 3
AND col1 > 'B'
каков будет правильный результат? Вы ожидали бы, что col1 > 'B'
предикат выполнялся до или после нумерации строк?
нет необходимости в CTE, просто используйте функцию windowing в подзапросе:
select id, sales_person_id, product_type, product_id, sale_amount
from
(
select id, sales_person_id, product_type, product_id, sale_amount,
row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc) rn
from Sales_Log
) sl
where rn = 1
Edit, перемещая мой комментарий к ответу.
оконные функции не выполняются, пока данные фактически не выбраны, что после WHERE
предложения. Поэтому, если вы попытаетесь использовать row_number
на WHERE
предложение значение еще не назначено.
прежде всего это что-то называется all-at-once operation
"все сразу операции" означает, что все выражения в одном и том же фаза процесса логического запроса оценивается логически одновременно.
и большую главу влияние на оконные функции:
Предположим, у вас есть:
CREATE TABLE #Test ( Id INT) ;
INSERT INTO #Test VALUES ( 1001 ), ( 1002 ) ;
SELECT Id
FROM #Test
WHERE Id = 1002
AND ROW_NUMBER() OVER(ORDER BY Id) = 1;
все-сразу операции говорят нам, что эти два условия оцениваются логически на же момент времени. таким образом SQL Server может оценить условия в предложении WHERE в произвольном порядке на основе расчетный план исполнения. Итак, главный вопрос здесь в том, какое условие сначала оценивает.
Пример 1:
If ( Id = 1002 ) is first, then if ( ROW_NUMBER() OVER(ORDER BY Id) = 1 )
результат: 1002
Пример 2:
If ( ROW_NUMBER() OVER(ORDER BY Id) = 1 ), then check if ( Id = 1002 )
результат: пустые
Итак, у нас есть парадокс.
в этом примере показано, почему мы не можем использовать оконные функции в предложении WHERE. Вы можете больше подумать об этом и найти, почему функции окна разрешено использовать только в выберите и ЗАКАЗАТЬ ПО положения!
вам не обязательно использовать CTE, вы можете запросить результирующий набор после использования row_number ()
select row, id, sales_person_id, product_type, product_id, sale_amount
from (
select
row_number() over(partition by sales_person_id,
product_type, product_id order by sale_amount desc) AS row,
id, sales_person_id, product_type, product_id, sale_amount
from Sales_Log
) a
where row = 1
да к сожалению, когда вы делаете оконную функцию, SQL злится на вас, даже если ваш предикат where является законным. Вы создаете cte или вложенный select, имеющий значение в инструкции select, а затем ссылаетесь на CTE или вложенный select с этим значением позже. Простой пример, который должен быть самоочевидным. Если вы действительно ненавидите cte за некоторые проблемы с производительностью при выполнении большого набора данных, вы всегда можете перейти к временной таблице или переменной таблицы.
declare @Person table ( PersonID int identity, PersonName varchar(8));
insert into @Person values ('Brett'),('John');
declare @Orders table ( OrderID int identity, PersonID int, OrderName varchar(8));
insert into @Orders values (1, 'Hat'),(1,'Shirt'),(1, 'Shoes'),(2,'Shirt'),(2, 'Shoes');
--Select
-- p.PersonName
--, o.OrderName
--, row_number() over(partition by o.PersonID order by o.OrderID)
--from @Person p
-- join @Orders o on p.PersonID = o.PersonID
--where row_number() over(partition by o.PersonID order by o.orderID) = 2
-- yields:
--Msg 4108, Level 15, State 1, Line 15
--Windowed functions can only appear in the SELECT or ORDER BY clauses.
;
with a as
(
Select
p.PersonName
, o.OrderName
, row_number() over(partition by o.PersonID order by o.OrderID) as rnk
from @Person p
join @Orders o on p.PersonID = o.PersonID
)
select *
from a
where rnk >= 2 -- only orders after the first one.
наконец, есть старомодный способ до SQL Server 2005 с коррелированным подзапросом:
select *
from Sales_Log sl
where sl.id = (
Select Top 1 id
from Sales_Log sl2
where sales_person_id = sl.sales_person_id
and product_type = sl.product_type
and product_id = sl.product_id
order by sale_amount desc
)
это старая тема, но я постараюсь ответить конкретно на вопрос, выраженный в теме.
почему нет оконных функций в предложениях where?
SELECT
оператор имеет следующие основные пункты, указанные в keyed-в порядке:
SELECT DISTINCT TOP list
FROM JOIN ON / APPLY / PIVOT / UNPIVOT
WHERE
GROUP BY WITH CUBE / WITH ROLLUP
HAVING
ORDER BY
OFFSET-FETCH
Логический Порядок Обработки Запросов, или обязательный порядок, является концептуальная интерпретация порядка!--30-->, он определяет правильность запроса. Этот порядок определяет, когда объекты, определенные на одном шаге, становятся доступными для предложений на последующих шагах.
----- Relational result
1. FROM
1.1. ON JOIN / APPLY / PIVOT / UNPIVOT
2. WHERE
3. GROUP BY
3.1. WITH CUBE / WITH ROLLUP
4. HAVING
---- After the HAVING step the Underlying Query Result is ready
5. SELECT
5.1. SELECT list
5.2. DISTINCT
----- Relational result
----- Non-relational result (a cursor)
6. ORDER BY
7. TOP / OFFSET-FETCH
----- Non-relational result (a cursor)
например, если обработчик запросов может привязать (получить доступ) к таблицам или представлениям, определенным в FROM
предложение, эти объекты и их столбцы становятся доступными для всех последующих шагов.
и наоборот, все пункты, предшествующие SELECT
статья не может ссылаться на псевдонимы столбцов или производные столбцы, определенные в SELECT
предложения. Однако, те на столбцы могут ссылаться последующие предложения, такие как ORDER BY
предложения.
OVER
предложение определяет разбиение и порядок набора строк до применения соответствующей функции окна. То есть OVER
предложение определяет окно или заданный пользователем набор строк в Базовый Результат Запроса set и функция окна вычисляет результат против этого окна.
Msg 4108, Level 15, State 1, …
Windowed functions can only appear in the SELECT or ORDER BY clauses.
причина в том, что путь, как Логическая Обработка Запросов работает в T-SQL
. С базовый результат запроса устанавливается только тогда, когда логическая обработка запросов достигает SELECT
шаг 5.1. (то есть, после обработки FROM
, WHERE
, GROUP BY
и HAVING
шаги), оконные функции разрешены только в SELECT
и ORDER BY
пункты запроса.
обратите внимание, что оконные функции по-прежнему являются частью реляционного слоя, даже реляционная модель не имеет дело с данные заказа. Результат после SELECT
шаг 5.1. с любой функцией window по-прежнему реляционная.
кроме того, строго говоря, причина, по которой функция окна не разрешена в WHERE
предложение не потому, что это создаст двусмысленность, а потому, что порядок how Логическая Обработка Запросов процессы SELECT
заявление T-SQL
.