MS SQL Округление к ближайшему чётному

107
29 октября 2021, 12:10

Есть у меня логика в приложении на c#, которую я был вынужден начать дублировать на slq server в представлениях и хранимках и столкнулся с тем, что расчёт происходит различным образом.

Наглядный пример. Некоторая сущность с полем типа decimal, Code First, табличка на SQL Server, тип тоже decimal.

В C# Math.Round в случае числа с половинкой округляет данные к ближайшему чётному числу (MidpointRounding.ToEven):

decimal val1 = 766.5m;
decimal val2 = 767.5m;
Console.WriteLine(Math.Round(766.5m)); // 766
Console.WriteLine(Math.Round(767.5m)); // 768

При этом ROUND в SQL таких настроек не поддерживает:

DECLARE @val1 decimal = 766.5;
DECLARE @val2 decimal = 767.5;
SELECT ROUND(@val1,  0); -- 767
SELECT ROUND(@val2,  0); -- 768

Каким образом сделать поведение ROUND в MS SQL таким же, как в c#?

PS Версия сервера: MSSQL2016 (13.0.1601.5), .net core 2.1

Answer 1

Если я правильно понял задачу, то можно так:

DECLARE @T TABLE (col decimal(18,2))
INSERT @T (col)
VALUES (-122), (-122.5),
       (-123), (-123.5),
       (-124), (-124.5),
       (122), (122.5),
       (123), (123.5),
       (124), (124.5)
SELECT col
      ,IIF(CEILING(col) % 2 = 0, CEILING(col), FLOOR(col))
  FROM @T

Функция CEILING - возвращает наименьшее целое число, большее или равное переданному в неё числовому выражению;

Функция FLOOR - возвращает наибольшее целое число, меньшее или равное переданному в неё числовому выражению.

В выражении CEILING(col) % 2 = 0 мы проверяем число с откинутой дробной частью на чётность (остаток от деления на 2 должен быть равен нулю). Если число чётное, то берём наименьшее целое, в противном случае наибольшее целое.

Конструкцию IIF так же можно заменить на CASE для версий SQL меньше 2012, которые не поддерживают IIF:

CASE CEILING(col) % 2 WHEN 0 THEN CEILING(col) ELSE FLOOR(col) END

P.S. Запихивать эту конструкцию в функцию я бы не стал. Вообще в SQL Server от функций лучше держаться подальше без крайней нужды, так как они могут представлять собой подводные камни в плане производительности запросов.

READ ALSO
Отслеживание состояния переменной

Отслеживание состояния переменной

Нужно отслеживать состояние переменнойПытался сделать такой код:

161
Создание маски и ограничение ввода символов в TextBox?

Создание маски и ограничение ввода символов в TextBox?

Возникла необходимость создать ограничение для ввода данных в textBox

129
C# пространства имен

C# пространства имен

Решение содержит следующую структуру:

79
C# WPF Интегрирование элементов в Xaml

C# WPF Интегрирование элементов в Xaml

Есть основное окно MainWindowxaml

147