Вот мой конкретный ответ для 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
).
Без сомнения, есть и другие исключения, на которые я не наткнулся.