SQL舍入到最接近的百分之一

时间:2010-10-28 02:40:15

标签: sql ms-access

UPDATE prodfeatures2 SET featureprice = featureprice * 0.6316;

我正试图设置一轮到最接近的百分之一。帮助!

我不希望答案是104.7648,我希望它是104.76。

我不希望答案是104.7668,我希望它是104.77。

6 个答案:

答案 0 :(得分:4)

UPDATE prodfeatures2 SET featureprice = ROUND(featureprice * 0.6316,2)

答案 1 :(得分:2)

我认为您没有提供足够的数据来推断出您需要的rounding algorithm

如果您的规范告诉您使用哪种舍入算法,请发布它。

如果您的规范未能告诉您哪种舍入算法,那么请向设计师提出问题。

一般来说,SQL不是为数学计算而设计的。考虑在另一层进行舍入。如果这样做,请确保使用DECIMAL存储值,并在前端添加小数位。

答案 2 :(得分:2)

请参阅:How to Round in MS Access, VBA

引用摘录:

“Round函数执行round到even,这与round到large不同。” --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'))

答案 3 :(得分:1)

ROUND(TheValue, 2)

答案 4 :(得分:1)

SELECT ROUND(cast(104.7668 as decimal(6,2)),2)as roundof;

答案 5 :(得分:0)

这是我的MS-Access特定答案:这个问题有些可疑!

除非featureprice是一个非常大或非常大的小数量,并且元数据表明它不是,否则乘以0.6316之类的十进制文字会将结果强制转换为DECIMAL类型现在,在本质上,Access中的DECIMAL类型(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的值的数字运算符(加,减,乘和除)时,会将结果强制转换为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);

SQL DML:

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界面创建所有这些类型nad你可能不知道如何运行SQL DDL所以这里有一些vanilla 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。 Q.E.D。


之前提到的一些例外情况:

等于其INTEGER值的十进制文字,例如

SELECT TYPENAME(1.0)

返回'Long'(这是Jet 4.0的INTEGER类型的VBA等价物 - 为什么它显示VBA名称而不是我不知道的Jet名称。)

...除非值超出INTEGER范围:

SELECT TYPENAME(10000000000)

返回'Decimal'

...当值超出DECIMAL范围时激发:

SELECT TYPENAME(1E29)

返回'Double'(相当于Jet的FLOAT的VBA)。

在正范围内,使用DECIMAL字面值对值进行操作会将类型保留为FLOAT,例如

SELECT TYPENAME(1E29 + 0.1)

返回'Double ( FLOAT`)。

...而在负面范围内,它被赋予DECIMAL

SELECT TYPENAME(1E-29 + 0.1)

返回'Decimal'。

当穿越边界时,Coersion的工作方式不同,例如(注意INTEGER的上限是2,147,483,647):

SELECT TYPENAME(2147483648)

返回'Decimal`

...而:

SELECT TYPENAME(2147483647 + 1.0)

返回'Double'(FLOAT)。

毫无疑问,还有其他例外我没有偶然发现。