Получить файл строк (умножение)

SO,

Проблема

У меня проблема с умножением строк. В SQL существует функция SUM(), которая вычисляет сумму для некоторого поля для набора строк. Я хочу получить умножение, т.е. Для таблицы

+------+
| data |
+------+
|    2 |
|   -1 |
|    3 |
+------+

который будет 2*(-1)*3 = -6 в результате. Я использую тип данных DOUBLE для хранения моих значений данных.

Мой подход

Из школьной математики известно, что log(A x B) = log(A) + log(B) - так, чтобы можно было использовать для создания желаемого выражения, например:

SELECT
  IF(COUNT(IF(SIGN(`col`)=0,1,NULL)),0,
    IF(COUNT(IF(SIGN(`col`)<0,1,NULL))%2,-1,1)
    *
    EXP(SUM(LN(ABS(`col`))))) as product
FROM `test`;

- вы видите слабость этого метода - поскольку log(X) есть undefined, когда X<=0 - мне нужно подсчитать отрицательные знаки перед вычислением целого выражения. Пример данных и запросов для этого дается в этой скрипте. Еще одна слабость заключается в том, что нам нужно найти, если в столбцах есть 0 (поскольку это образец, в реальной ситуации я собираюсь выбрать продукт для некоторого подмножества строк таблицы с некоторым условием (состояниями) - т.е. я не могу просто удалите 0-s из моей таблицы, потому что результат нулевой продукт является допустимым и ожидаемым результатом для некоторых подмножеств строк)

Особенности

И теперь, наконец, мой основной вопрос: как обрабатывать ситуацию, когда у нас есть выражение типа: X*Y*Z и здесь X < MAXF, Y<MAXF, но X*Y>MAXF и X*Y*Z<MAXF - поэтому у нас есть возможные данные type overflow (здесь MAXF является лимитом для двойного типа данных MySQL). Образец здесь. Запрос выше работает хорошо, но могу ли я всегда быть уверенным, что он справится с этим должным образом? То есть может быть, есть еще один случай с проблемой переполнения, когда некоторые подпрограммы вызывают переполнение, но весь продукт в порядке (без переполнения).

Или может быть другой способ найти файл строк? Кроме того, в таблице могут быть миллионы записей (-1.1<X<=1.1 в основном, но, вероятно, с такими значениями, как 100 или 1000 - т.е. достаточно высокий, чтобы переполнить DOUBLE, если умножить на определенное количество, если у нас есть проблема, о которой я уже говорил выше) может быть вычисление через log будет медленным?

Ответ 1

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

Знаки могут храниться как 1 (для положительных элементов), -1 (для негативов) и 0 (для нуля.)

Логарифм может быть назначен для нуля как 0 (или любое другое значение), но он не должен использоваться в вычислениях.

Тогда вычисление будет:

SELECT 
    CASE WHEN EXISTS (SELECT 1 FROM test WHERE <condition> AND datasign = 0)
         THEN 0
         ELSE (SELECT 1-2*(SUM(datasign=-1)%2) FROM test WHERE <condition>)
    END AS resultsign,

    CASE WHEN EXISTS (SELECT 1 FROM test WHERE <condition> AND datasign = 0)
         THEN -1            -- undefined log for result 0
         ELSE (SELECT SUM(datalog) FROM test WHERE <condition> AND datasign <> 0)
    END AS resultlog
  ;

Таким образом, у вас нет проблем с переполнением. Вы можете проверить resultlog, если он превышает некоторые ограничения, или просто попытаться вычислить resultdata = resultsign * EXP(resultlog) и посмотреть, не возникает ли ошибка.

Ответ 2

Я думаю, это сработает...

SELECT IF(MOD(COUNT(data < 0),2)=1
        , EXP(SUM(LOG(data)))*-1
        , EXP(SUM(LOG(data))))
          x 
  FROM my_table;

Ответ 3

Этот вопрос замечательный в море низкого качества. Спасибо, даже чтение было приятным.

Точность

Идея exp(log(a)+log(b)) сама по себе хороша. Однако после прочтения "Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой" , убедитесь, что вы используете DECIMAL или NUMERIC, чтобы убедиться, что вы используете Precision Math, иначе ваши значения будут на удивление неточными. За пару миллионов строк ошибки могут складываться очень быстро! DECIMAL (согласно документу MySQL) имеет точность не более 65 цифр, в то время как, например, 64-битные значения с плавающей запятой IEEE754 имеют только до 16 цифр (точность log10 (2 ^ 52) = 15,65)!

Переполнение

В соответствии с соответствующей частью документа MySQL:

  • Переполнение
  • Целое приводит к бесшумному обходу.Переполнение
  • DECIMAL приводит к усеченному результату и предупреждению.
  • Переполнение с плавающей точкой дает результат NULL. Переполнение для некоторых операций может привести к + INF, -INF или NaN.

Таким образом, вы можете обнаружить переполнение с плавающей запятой, если это когда-либо произойдет.

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

Производительность

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