Порядок по столбцу, имеющему дубликаты для агрегации

#sql #sql-server #sum #sql-order-by #window-functions

#sql #sql-сервер #сумма #sql-порядок по #окно-функции

Вопрос:

Таблица сотрудников

 select name, gender, salary, sum(salary) over(order by salary) 
from Employees
  

Вопрос:
Почему упорядочение по столбцу, имеющему дубликаты, приводит к получению конечных значений вместо промежуточных значений?
Например, когда выполняется этот запрос, 3 сотрудника с зарплатой = 5000, конечное значение, то есть значение, которое должно быть создано для 3-го сотрудника, создается для 1-го?

Ответ №1:

Оконные функции SQL имеют предложение window frame, которое указывает, какие строки включены в оконную функцию. По умолчанию используется range between unbounded preceding and current row значение по умолчанию, поэтому ваш код действительно:

 sum(salary) over (order by salary range between unbounded preceding and current row)
  

range Разделение обрабатывает связи одинаково, поэтому все они включены во фрейм — или ни один не включен.

Альтернативой является row разделение, которое обрабатывает каждую строку индивидуально:

 sum(salary) over (order by salary row between unbounded preceding and current row)
  

Стандарт SQL указывает, что range это значение по умолчанию (я предполагаю, что результаты стабильны, см. Ниже). Итак, это значение по умолчанию во всех базах данных.

Когда зарплаты различны, они возвращают одинаковые значения. Однако они не всегда совпадают с тем, что показывает эта скрипка.

Обратите внимание, что при наличии связей и использовании фрейма окна rows результаты нестабильны. Это означает, что при разных запусках вы можете получать разные результаты для данной строки (и я могу добавить из личного опыта, что это очень, очень сложно отлаживать).

Вы можете решить эту проблему, включив уникальный ключ в the order by , что является решением, предложенным GMB.

Ответ №2:

Именно так работают функции aggregate window. Когда есть связи, все они получают одинаковое значение. То же самое касается count() , min() , max() , и так далее.

Если вам нужно другое значение в каждой строке, добавьте еще один столбец (или набор столбцов) в order by предложение, чтобы сделать его детерминированным (т. Е. Разорвать связи). Скажем, employee_id это первичный ключ в таблице, затем:

 select name, gender, salary, sum(salary) over(order by salary, employee_id) 
from Employees
order by salary, employee_id