Агрегирующий оператор умножения в SQL - PullRequest
25 голосов
/ 24 марта 2011

В SQL есть операторы агрегирования, такие как AVG, SUM, COUNT. Почему у него нет оператора для умножения? "MUL" или что-то.

Мне было интересно, существует ли он для Oracle, MSSQL, MySQL? Если нет, то есть ли обходной путь, который дал бы такое поведение?

Ответы [ 7 ]

41 голосов
/ 24 марта 2011

Под MUL вы подразумеваете прогрессивное умножение значений?

Даже при наличии 100 строк небольшого размера (скажем, 10 с) ваш MUL (столбец) будет переполнять любой тип данных! С такой высокой вероятностью неправильного / неправильного использования и очень ограниченной областью применения он не обязательно должен быть стандартом SQL. Как показали другие, существуют математические способы решения этой проблемы, так же как существует много способов сделать сложные вычисления в SQL, просто используя стандартные (и общепринятые) методы.

Пример данных:

Column
1
2
4
8

COUNT : 4 items (1 for each non-null)
SUM   : 1 + 2 + 4 + 8 = 15
AVG   : 3.75 (SUM/COUNT)
MUL   : 1 x 2 x 4 x 8 ? ( =64 )

Для полноты реализации ядра Oracle, MSSQL, MySQL *

Oracle : EXP(SUM(LN(column)))   or  POWER(N,SUM(LOG(column, N)))
MSSQL  : EXP(SUM(LOG(column)))  or  POWER(N,SUM(LOG(column)/LOG(N)))
MySQL  : EXP(SUM(LOG(column)))  or  POW(N,SUM(LOG(N,column)))
  • Будьте внимательны при использовании EXP / LOG в SQL Server, следите за типом возврата http://msdn.microsoft.com/en-us/library/ms187592.aspx
  • Форма POWER допускает большие числа (с использованием оснований, превышающих число Эйлера), а в тех случаях, когда результат становится слишком большим, чтобы вернуть его обратно с помощью POWER, вы можете вернуть только логарифмическое значение и вычислить фактическое число вне SQL-запрос


* LOG (0) и LOG (-ve) не определены. Ниже показано только, как справиться с этим в SQL Server. Эквиваленты можно найти для других разновидностей SQL, используя ту же концепцию

create table MUL(data int)
insert MUL select 1 yourColumn union all
           select 2 union all
           select 4 union all
           select 8 union all
           select -2 union all
           select 0

select CASE WHEN MIN(abs(data)) = 0 then 0 ELSE
       EXP(SUM(Log(abs(nullif(data,0))))) -- the base mathematics
     * round(0.5-count(nullif(sign(sign(data)+0.5),1))%2,0) -- pairs up negatives
       END
from MUL

Состав:

  • с учетом abs () данных, если min равно 0, умножить на все остальное бесполезно, результат равен 0
  • Когда данные равны 0, NULLIF преобразует их в ноль. Оба abs (), log () возвращают null, исключая его из sum ()
  • Если данные не равны 0, abs позволяет нам умножать отрицательное число с помощью метода LOG - мы будем отслеживать отрицательность в других местах
  • Разработка окончательного знака
    • знак (данные) возвращает 1 for >0, 0 for 0 и -1 for <0.
    • Мы добавили еще 0,5 и снова взяли знак (), поэтому теперь мы классифицировали 0 и 1 как 1, и только -1 как -1.
    • снова используйте NULLIF для удаления из COUNT () единиц, поскольку нам нужно только подсчитать негативы.
    • % 2 против количества () отрицательных чисел возвращает либо
    • -> 1, если есть нечетное число отрицательных чисел
    • -> 0, если есть четное число отрицательных чисел
    • больше математических трюков: мы берем 1 или 0 из 0,5, так что выше становится
    • -> (0.5-1=-0.5 => округлить до -1 ), если существует нечетное число отрицательных чисел
    • -> (0.5-0= 0.5 => округлить до 1 ), если существует четное число отрицательных чисел
    • мы умножаем это окончательное 1 / -1 на значение SUM-PRODUCT для реального результата
24 голосов
/ 24 марта 2011

Нет, но вы можете использовать математику:)

, если yourColumn всегда больше нуля:

select EXP(SUM(LOG(yourColumn))) As ColumnProduct from yourTable
7 голосов
/ 24 марта 2011

Я вижу, что ответ Oracle все еще отсутствует, поэтому вот оно:

SQL> with yourTable as
  2  ( select 1 yourColumn from dual union all
  3    select 2 from dual union all
  4    select 4 from dual union all
  5    select 8 from dual
  6  )
  7  select EXP(SUM(LN(yourColumn))) As ColumnProduct from yourTable
  8  /

COLUMNPRODUCT
-------------
           64

1 row selected.

С уважением,
Роб.

1 голос
/ 24 марта 2011

Вы разбьете любой тип данных довольно быстро, как только число увеличится.

Использование LOG / EXP равно хитро из-за чисел <= 0, которые не пройдут при использовании LOG. Я написал решение в <a href="/3256170/sql-server-query-gruppovoe-umnozhenie#3256174"> этом вопросе , которое имеет дело с этим

1 голос
/ 24 марта 2011

С PostgreSQL вы можете создавать свои собственные агрегатные функции, см. http://www.postgresql.org/docs/8.2/interactive/sql-createaggregate.html

Чтобы создать агрегатную функцию в MySQL, вам нужно создать файл .so (linux) или .dll (windows). Пример показан здесь: http://www.codeproject.com/KB/database/mygroupconcat.aspx

Я не уверен насчет mssql и oracle, но держу пари, что у них есть опции для создания пользовательских агрегатов.

0 голосов
/ 08 ноября 2013

Использование CTE в MS SQL:

CREATE TABLE Foo(Id int, Val int)
INSERT INTO Foo VALUES(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)

;WITH cte AS 
(
    SELECT Id, Val AS Multiply, row_number() over (order by Id) as rn
    FROM Foo
    WHERE Id=1
    UNION ALL
    SELECT ff.Id, cte.multiply*ff.Val as multiply, ff.rn FROM
    (SELECT f.Id, f.Val, (row_number() over (order by f.Id)) as rn
    FROM Foo f) ff
        INNER JOIN cte
        ON ff.rn -1= cte.rn
)
SELECT * FROM cte
0 голосов
/ 24 марта 2011

Не уверен насчет Oracle или sql-сервера, но в MySQL вы можете просто использовать *, как обычно.

mysql> select count(id), count(id)*10 from tablename;
+-----------+--------------+
| count(id) | count(id)*10 |
+-----------+--------------+
|       961 |         9610 |
+-----------+--------------+
1 row in set (0.00 sec)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...