SQL раунд с точностью до сотых - PullRequest
3 голосов
/ 28 октября 2010
UPDATE prodfeatures2 SET featureprice = featureprice * 0.6316;

Я пытаюсь установить раунд с точностью до сотых. ПОМОГИТЕ! * * 1002

Я не хочу, чтобы ответ был 104,7648, я бы хотел, чтобы он был 104,76.

Я не хочу, чтобы ответ был 104,7668, я бы хотел, чтобы он был 104,77.

Ответы [ 6 ]

4 голосов
/ 28 октября 2010
UPDATE prodfeatures2 SET featureprice = ROUND(featureprice * 0.6316,2)
2 голосов
/ 28 октября 2010

Пожалуйста, смотрите: Как округлить в MS Access, VBA

Цитировать выдержку:

"Функция округления выполняет округление до четного, что отличается отот круглого к большему. "--Microsoft

Формат всегда округляется.

  Debug.Print Round(19.955, 2)
  'Answer: 19.95

  Debug.Print Format(19.955, "#.00")
  'Answer: 19.96

В этом случае:

UPDATE prodfeatures2 SET featureprice = CCUR(Format(featureprice * 0.6316,'#.00'))
2 голосов
/ 28 октября 2010

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

Если ваша спецификация говорит вам, какой алгоритм округления использовать, пожалуйста, опубликуйте его.

Если ваша спецификация не сообщает вам, какой алгоритм округления, тогда поднимите проблему с конструктором.

Вообще говоря, SQL не предназначен для математических вычислений. Рассмотрите возможность округления на другом уровне. Если вы это сделаете, обязательно сохраняйте значения, используя DECIMAL с дополнительным десятичным знаком, чем это требуется во внешнем интерфейсе.

1 голос
/ 19 декабря 2012

ВЫБЕРИТЕ КРУГЛЫЙ (приведение (104,7668 к десятичному (6,2)), 2) к круглому;

1 голос
/ 28 октября 2010

ROUND(TheValue, 2)

0 голосов
/ 29 октября 2010

Вот мой конкретный ответ для MS-Access: в этом вопросе есть что-то подозрительное!

Если featureprice не является чрезвычайно большим или очень большим небольшим количеством, и метаданные предполагают, что это не так, умножая надесятичный литерал, такой как 0.6316, приведёт результат к типу DECIMAL. Теперь, по своей природе, тип DECIMAL в Access (ACE, Jet, что угодно) округляет усечением, например, если вы можете сделать это:

SELECT CAST(104.7668 AS DECIMAL(17, 2)

округляется до 104.76 ... конечно, вы не можете сделать это в Access, поскольку он не поддерживает синтаксис стандарта SQL и собственный проприетарный синтаксис CDEC()был сломан с первого дня и до сих пор не исправлен в ACE (закатывает глаза).Но вы можете сделать следующее:

CREATE TABLE TestDecimal 
(
 dec_col DECIMAL(17, 2) NOT NULL UNIQUE
);

INSERT INTO TestDecimal (dec_col) 
   VALUES (104.7668);

SELECT dec_col FROM TestDecimal;
-- 104.76 -- truncated

Я собираюсь предположить, что ваш столбец prodfeatures2 имеет тип CURRENCY, и я предлагаю, чтобы, если вы не хотите, чтобы ваш результат был представлен какDECIMAL, и что мы можем сказать из вашего алгоритма, что вы этого не сделаете, тогда ваш SQL пропускает приведение.

Далее, вы хотите, чтобы результат был двумя десятичными разрядами, но исходные значения не равны двумдесятичные разряды.Например:

SELECT CCUR(CCUR(165.87) * 0.6316)
-- 104.7635 -- too low

SELECT CCUR(CCUR(165.88) * 0.6316)
-- 104.7698 -- too high

SELECT CCUR(CCUR(165.872) * 0.6316)
-- 104.7648 -- spot on

Таким образом, значения не могут быть округлены до двух dp более ранним процессом, но должны быть два dp после этого процесса?Как я уже сказал, здесь может что-то пахнуть, и у вас есть ошибки, которые вы еще не обнаружили ... или есть нечто большее, чем вы раскрываете здесь.


Что является основой дляВаше утверждение о том, что умножение на десятичное число приводит результат к десятичному типу данных?

(Язык в щеке) Почему, конечно, я прочитал это в руководстве пользователя для ACE / Jet.Только шучу, нет ни одного.Как и все в Jet 4.0, у вас есть только эксперимент.

Десятичные литералы (за исключением, например, очень больших и очень маленьких значений) имеют тип DECIMAL.Например:

SELECT TYPENAME(0.1)

возвращает значение «Десятичное число».

При использовании числовых операторов (сложение, вычитание, умножение и деление) со значением типа DECIMAL приведёт результат квведите DECIMAL (применимы те же исключения, что и выше).

Простой, но эффективный тест - создать таблицу с одним столбцом для каждого из числовых типов данных, вставить небольшое значение (скажем, 1) для каждогозатем добавьте / вычтите / умножьте / разделите все на десятичный литерал (скажем, 0,1):

SQL DDL:

CREATE TABLE TestNumericDataTypes
(
 TINYINT_col TINYINT NOT NULL, 
 SMALLINT_col SMALLINT NOT NULL, 
 INTEGER_col INTEGER NOT NULL, 
 REAL_col REAL NOT NULL, 
 FLOAT_col FLOAT NOT NULL, 
 DECIMAL_col DECIMAL NOT NULL, 
 CURRENCY_col CURRENCY NOT NULL, 
 YESNO_col YESNO NOT NULL, 
 DATETIME_col DATETIME  NOT NULL
);

SQL DML:

INSERT INTO TestNumericDataTypes 
(
 TINYINT_col, SMALLINT_col, INTEGER_col, 
 REAL_col, FLOAT_col, DECIMAL_col, 
 CURRENCY_col, YESNO_col, DATETIME_col
) 
VALUES (1, 1, 1, 1, 1, 1, 1, 1, 1);

SQLDML:

SELECT TYPENAME(TINYINT_col * 0.1), 
       TYPENAME(SMALLINT_col * 0.1), 
       TYPENAME(INTEGER_col * 0.1), 
       TYPENAME(REAL_col * 0.1), 
       TYPENAME(FLOAT_col * 0.1), 
       TYPENAME(DECIMAL_col * 0.1), 
       TYPENAME(CURRENCY_col * 0.1), 
       TYPENAME(YESNO_col * 0.1), 
       TYPENAME(DATETIME_col * 0.1),
       TYPENAME(TINYINT_col / 0.1), 
       TYPENAME(SMALLINT_col / 0.1), 
       TYPENAME(INTEGER_col / 0.1), 
       TYPENAME(REAL_col / 0.1), 
       TYPENAME(FLOAT_col / 0.1), 
       TYPENAME(DECIMAL_col / 0.1), 
       TYPENAME(CURRENCY_col / 0.1), 
       TYPENAME(YESNO_col / 0.1), 
       TYPENAME(DATETIME_col / 0.1),
       TYPENAME(TINYINT_col + 0.1), 
       TYPENAME(SMALLINT_col + 0.1), 
       TYPENAME(INTEGER_col + 0.1), 
       TYPENAME(REAL_col + 0.1), 
       TYPENAME(FLOAT_col + 0.1), 
       TYPENAME(DECIMAL_col + 0.1), 
       TYPENAME(CURRENCY_col + 0.1), 
       TYPENAME(YESNO_col + 0.1), 
       TYPENAME(DATETIME_col + 0.1),
       TYPENAME(TINYINT_col - 0.1), 
       TYPENAME(SMALLINT_col - 0.1), 
       TYPENAME(INTEGER_col - 0.1), 
       TYPENAME(REAL_col - 0.1), 
       TYPENAME(FLOAT_col - 0.1), 
       TYPENAME(DECIMAL_col - 0.1), 
       TYPENAME(CURRENCY_col - 0.1), 
       TYPENAME(YESNO_col - 0.1), 
       TYPENAME(DATETIME_col - 0.1)
FROM TestNumericDataTypes;

Я не уверен, что вы можете создать все эти типы через интерфейс Access, хотя вы, возможно, не знаете, как запустить SQL DDL, так что вот несколько ванильных VBA (доступ не требуется, например, можно запуститьиз Excel, ссылки не требуются, например, просто скопируйте и вставьте):

Sub TestAccessDecimals()

  On Error Resume Next
  Kill Environ$("temp") & "\DropMe.mdb"
  On Error GoTo 0

  Dim cat
  Set cat = CreateObject("ADOX.Catalog")
  With cat
    .Create _
        "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=" & _
        Environ$("temp") & "\DropMe.mdb"
    With .ActiveConnection

      Dim Sql As String
      Sql = _
          "CREATE TABLE TestNumericDataTypes" & vbCr & "(" & vbCr & " TINYINT_col TINYINT NOT NULL, " & vbCr & " SMALLINT_col SMALLINT NOT NULL, " & vbCr & " INTEGER_col INTEGER NOT NULL, " & vbCr & " REAL_col REAL NOT NULL, " & vbCr & " FLOAT_col FLOAT NOT" & _
          " NULL, " & vbCr & " DECIMAL_col DECIMAL NOT NULL, " & vbCr & " CURRENCY_col CURRENCY NOT NULL, " & vbCr & " YESNO_col YESNO NOT NULL, " & vbCr & " DATETIME_col DATETIME  NOT NULL" & vbCr & ");"
      .Execute Sql

      Sql = _
          "INSERT INTO TestNumericDataTypes " & vbCr & "(" & vbCr & " TINYINT_col, SMALLINT_col, INTEGER_col, " & vbCr & " REAL_col, FLOAT_col, DECIMAL_col, " & vbCr & " CURRENCY_col, YESNO_col, DATETIME_col" & vbCr & ") " & vbCr & "VALUES (1, 1, 1, 1, 1, 1," & _
          " 1, 1, 1);"
      .Execute Sql

      Sql = _
          "SELECT TYPENAME(TINYINT_col * 0.1), " & vbCr & "       TYPENAME(SMALLINT_col * 0.1), " & vbCr & "       TYPENAME(INTEGER_col * 0.1), " & vbCr & "       TYPENAME(REAL_col * 0.1), " & vbCr & "       TYPENAME(FLOAT_col * 0.1)," & _
          " " & vbCr & "       TYPENAME(DECIMAL_col * 0.1), " & vbCr & "       TYPENAME(CURRENCY_col * 0.1), " & vbCr & "       TYPENAME(YESNO_col * 0.1), " & vbCr & "       TYPENAME(DATETIME_col * 0.1)," & vbCr & "       TYPENAME(TINYINT_col / 0.1)," & _
          " " & vbCr & "       TYPENAME(SMALLINT_col / 0.1), " & vbCr & "       TYPENAME(INTEGER_col / 0.1), " & vbCr & "       TYPENAME(REAL_col / 0.1), " & vbCr & "       TYPENAME(FLOAT_col / 0.1), " & vbCr & "       TYPENAME(DECIMAL_col / 0.1)," & _
          " " & vbCr & "       TYPENAME(CURRENCY_col / 0.1), " & vbCr & "       TYPENAME(YESNO_col / 0.1), " & vbCr & "       TYPENAME(DATETIME_col / 0.1)," & vbCr & "       TYPENAME(TINYINT_col + 0.1), " & vbCr & "       TYPENAME(SMALLINT_col +" & _
          " 0.1), " & vbCr & "       TYPENAME(INTEGER_col + 0.1), " & vbCr & "       TYPENAME(REAL_col + 0.1), " & vbCr & "       TYPENAME(FLOAT_col + 0.1), " & vbCr & "       TYPENAME(DECIMAL_col + 0.1), " & vbCr & "       TYPENAME(CURRENCY_col" & _
          " + 0.1), " & vbCr & "       TYPENAME(YESNO_col + 0.1), " & vbCr & "       TYPENAME(DATETIME_col + 0.1)," & vbCr & "       TYPENAME(TINYINT_col - 0.1), " & vbCr & "       TYPENAME(SMALLINT_col - 0.1), " & vbCr & "       TYPENAME(INTEGER_col" & _
          " - 0.1), " & vbCr & "       TYPENAME(REAL_col - 0.1), " & vbCr & "       TYPENAME(FLOAT_col - 0.1), " & vbCr & "       TYPENAME(DECIMAL_col - 0.1), " & vbCr & "       TYPENAME(CURRENCY_col - 0.1), " & vbCr & "       TYPENAME(YESNO_col" & _
          " - 0.1), " & vbCr & "       TYPENAME(DATETIME_col - 0.1)" & vbCr & "FROM TestNumericDataTypes;"

      Dim rs
      Set rs = .Execute(Sql)
      MsgBox rs.GetString
    End With
    Set .ActiveConnection = Nothing
  End With
End Sub

В каждом случае получается Decimal.QED


Несколько исключений, на которые ссылались ранее:

Десятичные литералы, равные их INTEGER значению, например

SELECT TYPENAME(1.0)

возвращает 'Long' (что соответствует VBA типа INTEGER Jet 4.0 - почему он показывает имя VBA, а не имя Jet, которое я не знаю).

... кроме случаев, когдазначение выходит за пределы диапазона INTEGER:

SELECT TYPENAME(10000000000)

возвращает десятичное число

... исключая, когда значение выходит за пределы диапазона DECIMAL:

SELECT TYPENAME(1E29)

возвращает значение Double (эквивалент VBA для Jet 10 * *).

В положительном диапазоне при работе со значением с литералом DECIMAL тип сохраняется как FLOAT например,

SELECT TYPENAME(1E29 + 0.1)

возвращает 'Double (FLOAT`).

... в то время как в отрицательном диапазоне он приводится к DECIMAL

SELECT TYPENAME(1E-29 + 0.1)

, возвращает «десятичное число».

Coersion работает по-разному при пересечении границ, например (учитывая, что верхние границы для INTEGER равны 2 147 483 647):

SELECT TYPENAME(2147483648)

возвращает 'Десятичное число'

...тогда как:

SELECT TYPENAME(2147483647 + 1.0)

возвращает 'Double' (FLOAT).

Без сомнения, есть и другие исключения, на которые я не наткнулся.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...