Подтвердить что ты не робот

Почему в SQL нет агрегатной функции PRODUCT?

Im ищет что-то вроде SELECT PRODUCT(table.price) FROM table GROUP BY table.sale, похожее на то, как работает SUM.

Я что-то пропустил в документации или действительно нет функции PRODUCT?

Если да, почему бы и нет?

Примечание. Я искал функцию в postgres, mysql и mssql и не нашел ни одного, поэтому я предположил, что все sql не поддерживает его.

4b9b3361

Ответ 1

В стандарте SQL нет функции set PRODUCT. Он, кажется, был достойным кандидатом, хотя (в отличие от, скажем, функции CONCATENATE set: он не подходит для SQL, например, результирующий тип данных будет включать в себя многозначность и представлять проблему в отношении первой нормальной формы).

Стандарты SQL нацелены на консолидацию функциональных возможностей продуктов SQL примерно в 1990 году и обеспечение "лидерства мышления" в отношении будущего развития. Короче говоря, они документируют, что делает SQL и что должен делать SQL. Отсутствие функции set PRODUCT предполагает, что в 1990 году ни один поставщик, хотя и достойный включения, и академический интерес к его внедрению в Стандарт не представлял.

Конечно, поставщики всегда стремились добавить свою собственную функциональность, в наши дни обычно это скорее степени, чем стандарты, а не касательные. Я не помню, как функция PRODUCT set (или даже требование для одного) в любом из продуктов SQL, которые я использовал.

В любом случае, работа вокруг довольно проста, используя log и exp скалярные функции (и логику для обработки негативов) с помощью функции set SUM; см. ответ @gbn для некоторого примера кода. Однако мне никогда не приходилось делать это в бизнес-приложении.

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

Из интереса, действительно нет спроса в SQL Server Land для новых функций набора, но для разновидностей оконной функции (и Standard SQL тоже). Более подробную информацию, в том числе о том, как принять участие в дальнейшем востребованном спросе, см. блог Itzik Ben-Gan.

Ответ 2

Для MSSQL вы можете использовать это. Он может быть принят для других платформ: он просто математики и агрегаты по логарифмам.

SELECT
    GrpID,
    CASE
       WHEN MinVal = 0 THEN 0
       WHEN Neg % 2 = 1 THEN -1 * EXP(ABSMult)
       ELSE EXP(ABSMult)
    END
FROM
    (
    SELECT
       GrpID, 
       --log of +ve row values
       SUM(LOG(ABS(NULLIF(Value, 0)))) AS ABSMult,
       --count of -ve values. Even = +ve result.
       SUM(SIGN(CASE WHEN Value < 0 THEN 1 ELSE 0 END)) AS Neg,
       --anything * zero = zero
       MIN(ABS(Value)) AS MinVal
    FROM
       Mytable
    GROUP BY
       GrpID
    ) foo

Взято из моего ответа здесь: Запрос SQL Server - групповое умножение

Ответ 3

Я не знаю, почему его нет, но (более осторожно относитесь к отрицательным числам) вы можете использовать журналы и показатели: -

select exp (sum (ln (table.price))) from table ...

Ответ 4

Вы можете выполнять функцию агрегации продукта, но вы должны сами выполнять математику, как это...

SELECT
    Exp(Sum(IIf(Abs([Num])=0,0,Log(Abs([Num])))))*IIf(Min(Abs([Num]))=0,0,1)*(1-2*(Sum(IIf([Num]>=0,0,1)) Mod 2)) AS P
FROM
   Table1

Источник: http://productfunctionsql.codeplex.com/

Ответ 5

В T-SQL существует четкий трюк (не уверен, если он ANSI), который позволяет объединить строковые значения из набора строк в одну переменную. Похоже, он работает и для умножения:

declare @Floats as table (value float)
insert into @Floats values (0.9)
insert into @Floats values (0.9)
insert into @Floats values (0.9)

declare @multiplier float = null

select 
    @multiplier = isnull(@multiplier, '1') * value
from @Floats

select @multiplier

Это потенциально может быть более численно устойчивым, чем решение log/exp.

Ответ 6

Я думаю, это потому, что никакая система нумерации не может разместить много продуктов. Поскольку базы данных предназначены для большого количества записей, продукт из 1000 номеров будет супермассивным, а в случае чисел с плавающей запятой распространенная ошибка будет огромной.

Также обратите внимание, что использование журнала может быть опасным решением. Хотя математически log (a * b) = log (a) * log (b), это может быть не в компьютерах, поскольку мы не имеем дело с действительными числами. Если вы вычисляете 2 ^ (log (a) + log (b)) вместо a * b, вы можете получить неожиданные результаты. Например:

SELECT 9999999999 * 99999999974482, EXP (LOG (9999999999) + LOG (99999999974482))

в Sql Server возвращает

999999999644820000025518, 9.99999999644812E + 23

Итак, моя точка зрения заключается в том, что вы пытаетесь сделать продукт тщательно и тщательно тестируете.

Ответ 7

Один из способов решения этой проблемы (если вы работаете на языке сценариев) - использовать функцию group_concat. Например, SELECT group_concat(table.price) FROM table GROUP BY table.sale

Это вернет строку со всеми ценами для одной и той же стоимости продажи, разделенной запятой. Затем с помощью парсера вы можете получить каждую цену и делать умножение. (В php вы можете даже использовать функцию array_reduce, фактически в руководстве php.net вы получите подходящий пример).

Приветствия

Ответ 8

Проблема может быть решена с использованием современных функций SQL, таких как оконные функции и CTE. Все является стандартным SQL и - в отличие от логарифмических решений - не требует переключения из целочисленного мира в мир с плавающей запятой или обработки неположительных чисел. Просто нумеруйте строки и оцените продукт в рекурсивном запросе, пока не останется ни одной строки:

   with recursive t(c) as (
     select unnest(array[2,5,7,8])
   ), r(c,n) as (
     select t.c, row_number() over () from t
   ), p(c,n) as (
     select c, n from r where n = 1
     union all
     select r.c * p.c, r.n from p join r on p.n + 1 = r.n
   )
   select c from p where n = (select max(n) from p);

Поскольку ваш вопрос касается группировки по столбцам продаж, все немного усложнилось, но все еще решаемо:

   with recursive t(sale,price) as (
     select 'multiplication', 2 union
     select 'multiplication', 5 union
     select 'multiplication', 7 union
     select 'multiplication', 8 union
     select 'trivial', 1 union
     select 'trivial', 8 union
     select 'negatives work', -2 union
     select 'negatives work', -3 union
     select 'negatives work', -5 union
     select 'look ma, zero works too!', 1 union
     select 'look ma, zero works too!', 0 union
     select 'look ma, zero works too!', 2
   ), r(sale,price,n,maxn) as (
     select t.sale, t.price, row_number() over (partition by sale), count(1) over (partition by sale)
     from t
   ), p(sale,price,n,maxn) as (
     select sale, price, n, maxn
     from r where n = 1
     union all
     select p.sale, r.price * p.price, r.n, r.maxn
     from p
     join r on p.sale = r.sale and p.n + 1 = r.n
   )
   select sale, price
   from p
   where n = maxn
   order by sale;

Результат:

sale,price
"look ma, zero works too!",0
multiplication,560
negatives work,-30
trivial,8

Проверено на Postgres.

Ответ 9

Другой подход, основанный на том факте, что мощность декартового произведения есть произведение мощностей определенных множеств ;-)

⚠ ВНИМАНИЕ: Этот пример просто для развлечения и довольно академический, не используйте его в производстве! (кроме того, что это только для положительных и практически малых целых чисел) ⚠

with recursive t(c) as (
  select unnest(array[2,5,7,8])
), p(a) as (
  select array_agg(c) from t
  union all
  select p.a[2:]
  from p
  cross join generate_series(1, p.a[1])
)
select count(*) from p where cardinality(a) = 0;

Ответ 10

Один из подходов состоит в объединении двух встроенных функций:

SELECT table.sale, AVG(table.price) * COUNT(table.price) FROM table GROUP BY table.sale