Почему эта сумма процентов не составляет 100%? - PullRequest
3 голосов
/ 06 июля 2011

У меня есть серия времени вычислений в DB2 SQL DB, которая хранится в формате с плавающей запятой со значением по умолчанию 0,0.

Таблица, которая обновляется, выглядит следующим образом:

CREATE TABLE MY_CALC_DATA_TABLE
(
    CALCDATE                 TIMESTAMP,
    INDIV_CALC_DURATION_IN_S FLOAT WITH DEFAULT 0.0,
    CALC_TIME_PERCENTAGE     FLOAT WITH DEFAULT 0.0
)

Используя sproc.Я рассчитываю сумму следующим образом:

CREATE OR REPLACE PROCEDURE MY_SCHEMA.MY_SPROC (IN P_DATE TIMESTAMP)
    LANGUAGE SQL
    NO EXTERNAL ACTION
BEGIN
    DECLARE V_TOTAL_CALC_TIME_IN_S FLOAT DEFAULT 0.0;

    -- other stuff setting up and joining data

    -- Calculate the total time taken to perform the
    -- individual calculations

    SET V_TOTAL_CALC_TIME_IN_S =
        (
            SELECT
                SUM(C.INDIV_CALC_DURATION_IN_S)
            FROM
                MY_SCHEMA.MY_CALC_DATA_TABLE C
            WHERE
                C.CALCDATE = P_DATE
        )

    -- Now calculate each individual calculation's percentage
    -- of the toal time.

    UPDATE
        MY_SCHEMA.MY_CALC_DATA_TABLE C
    SET
        C.CALC_TIME_PERCENTAGE =
            (C.INDIV_CALC_DURATION_IN_S / V_TOTAL_CALC_TIME_IN_S) * 100
    WHERE
        C.CALCDATE = P_DATE;

END@

Проблема в том, что, когда я делаю сумму всех значений CALC_TIME_PERCENTAGE для указанного CALC_DATE, она всегда меньше 100%, а сумма равна 80% или70% для разных CALC_DATES.

Здесь мы говорим между 35k и 55k вычислениями с максимальным процентом индивидуальных вычислений от общей суммы, как вычислено выше, равным 11% и лотами вычислений вДиапазон 0,00000N%.

Для расчета общего процента я использую простой запрос:

SELECT
    SUM(C.CALC_TIME_PERCENTAGE)
FROM
    MY_SCHEMA.MY_CALC_DATA_TABLE C
WHERE
    C.CALCDATE = P_DATE;

Есть предложения?

Обновление: Перестановкарасчеткак предложено исправил проблему.Благодарю.Кстати, в DB2 FLOAT и DOUBLE одного типа.А теперь прочитайте предложенную статью о поплавках.

Ответы [ 3 ]

3 голосов
/ 06 июля 2011

Если бы поле C.INDIV_CALC_DURATION_IN_S было целым числом, я бы предположил, что это ошибка округления.Чтение еще раз, это не проблема, так как тип данных FLOAT.

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

SET
    C.CALC_TIME_PERCENTAGE =
        (C.INDIV_CALC_DURATION_IN_S * 100.0 / V_TOTAL_CALC_TIME_IN_S)

Но вы упомянули, что в расчете на определенную дату много строк, поэтомуможет быть ошибка округления из-за этого.Попробуйте с DOUBLE типом данных в обоих полях (или хотя бы с полем CALC_TIME_PERCENTAGE) и посмотрите, станет ли разница от 100% меньше.

Я не уверен, что DB2 имеет DECIMAL(x,y)тип данных.Это может быть более уместным в этом случае.


Другая проблема заключается в том, как найти сумму CALC_TIME_PERCENTAGE.Я полагаю, что вы (и все остальные) использовали бы:

        SELECT
            P_DATE, SUM(CALC_TIME_PERCENTAGE)
        FROM
            MY_SCHEMA.MY_CALC_DATA_TABLE C
        GROUP BY P_DATE

Таким образом, у вас нет возможности определить, в каком порядке будет выполняться суммирование.Может быть даже невозможно определить это, но вы можете попробовать:

        SELECT
            P_DATE, SUM(CALC_TIME_PERCENTAGE)
        FROM
          ( SELECT
                P_DATE, CALC_TIME_PERCENTAGE
            FROM
                MY_SCHEMA.MY_CALC_DATA_TABLE C
            ORDER BY P_DATE
                   , CALC_TIME_PERCENTAGE ASC
          ) AS tmp
        GROUP BY P_DATE

Оптимизатор может игнорировать внутреннее пространство ORDER BY, но оно того стоит.


Еще одна возможность дляэта большая разница состоит в том, что строки удаляются из таблицы между операциями UPDATE и SHOW percent SUM.

Вы можете проверить, происходит ли это, выполнив вычисления (без UPDATE) и суммируя:

        SELECT
            P_DATE
          , SUM( INDIV_CALC_DURATION_IN_S * 100.0 / T.TOTAL )                   
            AS PERCENT_SUM
        FROM
            MY_SCHEMA.MY_CALC_DATA_TABLE C
          , ( SELECT SUM(INDIV_CALC_DURATION_IN_S) AS TOTAL
              FROM MY_SCHEMA.MY_CALC_DATA_TABLE
            ) AS TMP
        GROUP BY P_DATE
2 голосов
/ 06 июля 2011

Может быть проблема округления. Попробуйте C.INDIV_CALC_DURATION_IN_S * 100 / V_TOTAL_CALC_TIME_IN_S вместо этого.

1 голос
/ 06 июля 2011

Если C.INDIV_CALC_DURATION_IN_S очень мало, но у вас большое количество строк (и, таким образом, V_TOTAL_CALC_TIME_IN_S становится большим по сравнению), тогда

(C.INDIV_CALC_DURATION_IN_S / V_TOTAL_CALC_TIME_IN_S) * 100

может потерять точность, особенно если вы используете FLOAT s.

Если это так, то изменение вычисления (как упоминалось в другом месте) на

(C.INDIV_CALC_DURATION_IN_S * 100) / V_TOTAL_CALC_TIME_IN_S 

должно увеличить общее количество, хотя это может не дать вам 100%

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

...