использование group by и агрегатной функции

#sql

#sql

Вопрос:

Я не новичок в SQL, но мне кажется, что я никогда полностью не понимаю SQL из-за отсутствия некоторых знаний. Мы все знаем, что нам нужно использовать функции aggregate при использовании group by, например, у нас есть таблица orderdetails (с комбинацией клавиш orderid и productid), чтобы написать запрос, который возвращает заказы с общим значением (количество * цена единицы), превышающим 10000, sql:

 select orderid, sum(qty*unitprice) AS totalvalue from Sales.OrderDetails
group by orderid
having sum(qty*unitprice) > 10000
 

как мы знаем, выбор происходит после группирования и наличия, поэтому, когда я прихожу к

 group by orderid
having sum(qty*unitprice) > 10000
 

мы уже сгруппировали все записи по orderid и суммируем всю стоимость, если бы мы могли представить как:

 orderid  null(a column with no name but contains the total value)

123456   11000.00
987654   12184.00
 

затем дело доходит до предложения select:

 select orderid, sum(qty*unitprice) AS totalvalue
 

поскольку у нас уже есть результат после предложения ‘having’,
так почему нам нужно выполнить ‘sum (qty * unitprice)’, чтобы снова выполнить сумму, разве это не излишне?

Еще одна вещь, которая меня очень беспокоит: при написании SQL предложение select предшествует предложению from, что очень неудобно, потому что вы хотите получить IntelliSense имен столбцов. Конечно, я могу сначала написать из caluse, а затем «вставить» предложение select, но это делает все логически обратным образом. Я единственный человек, у которого есть проблемы с этим? …странно : (

Ответ №1:

Это, безусловно, повторяющийся и, следовательно, нежелательный, но трудно избежать повторения. Вы могли бы использовать вложенные запросы:

 SELECT orderid, totalvalue
  FROM (SELECT orderid, SUM(qty * unitprice) AS totalvalue
          FROM sales.orderdetails
         GROUP BY orderid) AS order_value
 WHERE totalvalue > 10000
 

Вам нужно будет взглянуть на план оптимизации вашей СУБД, чтобы определить, есть ли значительное снижение производительности при выполнении этого таким образом, но это позволяет избежать повторения SUM(qty * price) выражения. В идеале оптимизатор будет вставлять предложение outer WHERE во внутренний запрос как предложение HAVING, но я бы не хотел гарантировать, что это так (и разные системы могут, вероятно, будут обрабатывать это по-разному).).

Комментарии:

1. Я знаю, что вложенный запрос работает, но просто не понимаю идею первого метода. Еще одна вещь, которая меня беспокоит, это то, что при написании SQL предложение select предшествует предложению from, что очень неудобно, потому что вы хотите получить IntelliSense имен столбцов, нет сомнений, что я могу сначала написать из caluse, а затем «вставить» предложение select, что делает вашу логику обратной … причудливой : (

Ответ №2:

Во-первых, некоторые базы данных допускают псевдонимы в HAVING , поэтому вы часто можете писать:

 select orderid, sum(qty*unitprice) AS totalvalue
from Sales.OrderDetails
group by orderid
having totalvalue > 10000;
 

Во-вторых, SQL — это декларативный язык, а не процедурный язык. То есть то, что выполняется, не похоже на инструкцию. Фактически, это то, что называется направленным ациклическим графом (почти во всех базах данных), в котором нет операторов, которые вы могли бы распознать по ключевым словам SQL.

Это означает, что два идентичных выражения могут вычисляться только один раз, даже если они появляются несколько раз в запросе. Или в некоторых случаях выражение может вообще не вычисляться (например, select выражения в exists / not exists подзапросы).

В-третьих, фактическое sum() вычисление (в данном случае) очень мало по сравнению с фактической работой, выполняемой запросом. В частности, проблема производительности заключается в упорядочивании данных для агрегирования, а не в фактической обработке функций агрегирования.