T- SQL - потеря точности с плавающей точкой при использовании процедур - PullRequest
0 голосов
/ 03 мая 2020

Я пытался создать процедуру T- SQL, в которой я суммирую весь ежемесячный доход семьи в переменную @prihodki и одновременно суммирую все ежемесячные расходы семьи в переменную @odhodki. Печать переменной внутри этой процедуры выдает правильный ответ, однако, когда я пытаюсь выполнить процедуру и сохранить возвращаемое значение в переменной, точность теряется.

CREATE PROCEDURE izracunajPromet_TaMesec
AS
    BEGIN
        SET NOCOUNT ON;
        DECLARE @prihodki float, @odhodki float;

        SELECT @prihodki = SUM(t.Vrednost) 
        FROM Transakcija t 
        WHERE YEAR(t.Datum) = YEAR(current_timestamp) 
          AND MONTH(t.Datum) = MONTH(current_timestamp) 
          AND t.ID_Družine = 1 
          AND t.Vrednost >= 0;

        SELECT @odhodki = SUM(t.Vrednost) 
        FROM Transakcija t 
        WHERE YEAR(t.Datum) = YEAR(current_timestamp) 
          AND MONTH(t.Datum) = MONTH(current_timestamp) 
          AND t.ID_Družine = 1 
          AND t.Vrednost <= 0;

        IF (@prihodki IS NOT NULL AND @odhodki IS NOT NULL)
            RETURN CAST((@prihodki + @odhodki) AS NUMERIC(10, 2));
        ELSE IF (@prihodki IS NOT NULL AND @odhodki IS NULL)
        BEGIN
            PRINT @prihodki;
            RETURN CAST(@prihodki AS float);
        END
        ELSE 
            RETURN CAST(@odhodki AS NUMERIC(10, 2));
    END

    RETURN 0;
go

DECLARE @rezultat float;

EXEC @rezultat = izracunajPromet_TaMesec;

PRINT @rezultat;

Процедура выводит значение 67.73, в то время как метод выполнения и хранения возвращает значение 67.

Я уже пробовал приводить и преобразовывать, но ни один из эти решения работали.

ПРИМЕЧАНИЕ. Сосредоточьтесь на части ELSE IF, потому что именно здесь я получаю эту потерю точности, и я предполагаю, что, как только она будет решена, я смогу обновить также части IF и ELSE.

Ответы [ 2 ]

1 голос
/ 03 мая 2020

Как объяснено в Возвращение данных с использованием кода возврата :

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

Просмотрите остальную часть документа («Возвращение данных из хранимой процедуры»), чтобы узнать, как вернуть нецелые данные.


Что касается улучшения кода, существует несколько возможностей. Кажется более естественным сделать эту функцию определенной пользователем, но я сохранил хранимую процедуру и добавил для результата параметр output. Не ясно, почему значения суммируются отдельно на основе их знаков, а затем суммируются, когда простая сумма дает тот же результат. (Единственное преимущество - ошибочный оператор print, если нет неположительных значений.) Обратите внимание, что условие where выполнено sargable , так что индекс можно использовать для повышения производительности.

create procedure izracunajPromet_TaMesec
  @Result as Numeric(10,2) output
  as
  begin

  set nocount on

  declare @prihodki as Numeric(10,2), @odhodki as Numeric(10,2);

  -- Get the current date/time.
  declare @Now as DateTime = Current_Timestamp;
  -- Calculate the first date of the current month.
  declare @MonthStart as Date = DateAdd( day, 1 - Day( @Now ), @Now );
  -- Calculate the first date of the next month.
  declare @NextMonthStart as Date = DateAdd( month, 1, @MonthStart );

  select
    -- Use conditional aggregation to get both sums from a single pass through the data.
    @prihodki = sum( case when Vrednost >= 0 then Vrednost end ),
    @odhodki = sum( case when Vrednost <= 0 then Vrednost end ) 
    from Transakcija
    where
      -- Filter the date(time) using a half-open interval so that an index can be used.
      @MonthStart <= Datum and Datum < @NextMonthStart and
      ID_Družine = 1;

    -- If either value is   NULL   then substitute   0.0   and return the result.
    set @Result = Coalesce( @prihodki, 0.0 ) + Coalesce( @odhodki, 0.0 );

    return;

  end;

Пример использования:

declare @MyResult as Numeric(10,2);
execute izracunajPromet_TaMesec @Result = @MyResult output;
select @MyResult;
0 голосов
/ 03 мая 2020

Поработав некоторое время, у меня наконец возникла идея вернуть значение в виде таблицы и извлечь ее из нее. Это может или не может быть идеальным решением, но это работает. Я открыт для оптимизации.

CREATE PROCEDURE izracunajPromet_TaMesec
AS
    BEGIN
        SET NOCOUNT ON;
        DECLARE @result TABLE (Rezultat float);
        DECLARE @prihodki float, @odhodki float;

        SELECT @prihodki = SUM(t.Vrednost) 
        FROM Transakcija t 
        WHERE YEAR(t.Datum) = YEAR(current_timestamp) 
          AND MONTH(t.Datum) = MONTH(current_timestamp) 
          AND t.ID_Družine = 1 
          AND t.Vrednost >= 0;

        SELECT @odhodki = SUM(t.Vrednost) 
        FROM Transakcija t 
        WHERE YEAR(t.Datum) = YEAR(current_timestamp) 
          AND MONTH(t.Datum) = MONTH(current_timestamp) 
          AND t.ID_Družine = 1 
          AND t.Vrednost <= 0;

        IF @prihodki IS NULL
            SET @prihodki = 0;
        IF @odhodki IS NULL
            SET @odhodki = 0;

        INSERT INTO @result VALUES (@prihodki + @odhodki);
        SELECT * FROM @result;
    END

    RETURN 0;
go

DECLARE @result TABLE (Rezultat float);
DECLARE @numres float;
INSERT @result exec izracunajPromet_TaMesec;
SELECT @numres = Rezultat FROM @result;
PRINT @numres;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...