Почему нет оконных функций в предложениях 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.

ссылки: здесь, здесь и здесь