简体   繁体   English

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

[英]SQL round to the nearest hundredth

UPDATE prodfeatures2 SET featureprice = featureprice * 0.6316;

I am trying to setup a round of to the nearest hundredth. 我想设置一轮到最接近的百分之一。 HELP! 救命!

I do not want the answer to be 104.7648, I would want it to be 104.76. 我不希望答案是104.7648,我希望它是104.76。

I do not want the answer to be 104.7668, I would want it to be 104.77. 我不希望答案是104.7668,我希望它是104.77。

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

I don't think you have provided enough data to deduce which rounding algorithm you require. 我认为您没有提供足够的数据来推断出您需要哪种舍入算法

If your spec tells you which rounding algorithm to use then please post it. 如果您的规范告诉您使用哪种舍入算法,请发布它。

If your spec fails to tell you which rounding algorithm then raise the issue with the designer. 如果您的规范未能告诉您哪种舍入算法,那么请向设计人员提出问题。

Generally speaking, SQL isn't designed for math calculations. 一般来说,SQL不是为数学计算而设计的。 Consider doing rounding in another tier. 考虑在另一层进行舍入。 If you do, be sure to store values using DECIMAL with an additional decimal place than required in the front end. 如果这样做,请确保使用DECIMAL存储值,并在前端添加小数位。

Please see: How to Round in MS Access, VBA 请参阅: 如何在MS Access中进行回合,VBA

To quote an excerpt: 引用一段摘录:

"The Round function performs round to even, which is different from round to larger." “Round函数执行round到even,这不同于round到large。” --Microsoft - 微软

Format always rounds up. 格式总是向上舍入。

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

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

In this case: 在这种情况下:

UPDATE prodfeatures2 SET featureprice = CCUR(Format(featureprice * 0.6316,'#.00'))

SELECT ROUND(强制转换(104.7668为十进制(6,2)),2)为roundof;

ROUND(TheValue, 2)

Here's my MS-Access specific answer: there's something fishy about the question! 这是我的MS-Access特定答案:这个问题有些可疑!

Unless featureprice is an extremely large or extremely large small amount, and the metadata suggests that it is not, multiplying by a decimal literal such as 0.6316 will coerse the result to type DECIMAL .Now, by nature, the DECIMAL type in Access (ACE, Jet, whatever), rounds by truncation eg if you could do this: 除非featureprice是一个非常大或非常大的小数量,并且元数据表明它不是,否则乘以十进制文字(如0.6316会将结果0.6316转换为DECIMAL 。现在,在Access中的DECIMAL类型(ACE, Jet,无论如何),通过截断进行舍入,例如,如果你能做到这一点:

SELECT CAST(104.7668 AS DECIMAL(17, 2)

it would round to 104.76 ... of course you can't do this in Access because it doesn't support the SQL Standard syntax and its own proprietary syntax CDEC() was broken from day one and still hasn't been fixed in ACE (rolls eyes). 它会转到104.76 ...当然你不能在Access中执行此操作,因为它不支持SQL标准语法,并且它自己的专有语法CDEC()从第一天开始就被破坏,但仍然没有在ACE中修复(转动眼球)。 But what you can do is this: 但你能做的就是:

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

I'm going to guess that your prodfeatures2 column is type CURRENCY and I suggest that if don't want your result to be cast as a DECIMAL , and what we can tell from your algorithm you do not, then your SQL is missing a cast. 我猜你的prodfeatures2列是CURRENCY类型,我建议如果不希望你的结果被转换为DECIMAL ,我们可以从你的算法中判断出你没有,那么你的SQL缺少一个强制转换。

Further, you want the result to be two decimal places, yet the original values are not to two decimal places. 此外,您希望结果是两位小数,但原始值不是两位小数。 For example: 例如:

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

So the values are failing to be rounded to two dp by an earlier process but needs to be two dp after this process? 所以这个值不能通过早期的过程四舍五入到两个dp,但在这个过程之后需要两个dp? As I say, something may smell here and you have bugs you haven't yet tracked down... or there's more to this than you are revealing here. 正如我所说的,这里可能会有一些东西闻到你的臭虫还有你尚未追踪到的东西......或者还有更多的东西比你在这里透露的还要多。


What is the basis for your assertion that multipying by a decimal coerces the result to a decimal data type? 断言乘以十进制将结果强制为十进制数据类型的基础是什么?

(Tongue in cheek) Why, I read it in the user manual for ACE/Jet of course. (舌头在脸上)为什么,我当然在ACE / Jet的用户手册中读到它。 Only joking, there isn't one. 只是在开玩笑,没有一个。 Like anything in Jet 4.0, you just have experiment. 像Jet 4.0中的任何东西一样,你只需要进行实验。

Decimal literals (with exceptions eg extremely large and extremely small values) are of type DECIMAL . 十进制文字(例外,例如极大和极小的值)是DECIMAL类型。 For example: 例如:

SELECT TYPENAME(0.1)

returns 'Decimal'. 返回'十进制'。

When using the numeric operators (add, subtract, multiply and divide) involving a value of type DECIMAL will coerce the result to type DECIMAL (the same exceptions as above apply). 当使用涉及DECIMAL类型值的数字运算符(加,减,乘和除)时,将强制结果键入DECIMAL (与上述相同的例外情况适用)。

A simple yet effective test is to create a table with one column for each of the numeric data types, insert a small value (say 1) for each, then add/subtract/multiply/divide all by a decimal literal (say 0.1): 一个简单而有效的测试是为每个数值数据类型创建一个包含一列的表,为每个数据类型插入一个小值(比如1),然后用十进制文字(比如0.1)加/减/乘/除所有:

SQL DDL: 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: 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: 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;

I'm not sure whether you can create all these types via the Access interface nad you may not know how to run SQL DDL so here's some vanilla VBA (Access not required eg can be run from Excel, no references required eg just copy and paste): 我不确定你是否可以通过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

The result is Decimal in every case. 在每种情况下,结果都是Decimal QED QED


A few exceptions alluded to earlier: 前面提到的一些例外情况:

Decimal literals which are equal to their INTEGER value eg 十进制文字,等于它们的INTEGER值,例如

SELECT TYPENAME(1.0)

returns 'Long' (which is the VBA equivalent of Jet 4.0's INTEGER type -- why it shows the VBA name and not the Jet name I don't know). 返回'Long'(这是Jet 4.0的INTEGER类型的VBA等价物 - 为什么它显示VBA名称而不是我不知道的Jet名称)。

...except when the value is beyond the INTEGER range: ...除非值超出INTEGER范围:

SELECT TYPENAME(10000000000)

returns 'Decimal' 返回'十进制'

...excpet when the value is beyond the DECIMAL range: ...当值超出DECIMAL范围时激励:

SELECT TYPENAME(1E29)

returns 'Double' (being the VBA equivalent of Jet's FLOAT ). 返回'Double'(相当于Jet的FLOAT的VBA)。

In the positive range, operating on the value with a DECIMAL literal retains the type as FLOAT eg 在正范围内,使用DECIMAL文字对值进行操作DECIMAL类型保留为FLOAT例如

SELECT TYPENAME(1E29 + 0.1)

returns 'Double ( FLOAT`). 返回'Double ( FLOAT`)。

...whereas in the negative range it is coersed to DECIMAL ......在负面范围内,它被赋予了DECIMAL

SELECT TYPENAME(1E-29 + 0.1)

returns 'Decimal'. 返回'十进制'。

Coersion works differently when crossing bounds eg (noting that the upper bounds for INTEGER is 2,147,483,647): 穿越边界时Coersion的工作方式不同,例如(注意INTEGER的上限是2,147,483,647):

SELECT TYPENAME(2147483648)

returns 'Decimal` 返回'十进制'

...whereas: ...然而:

SELECT TYPENAME(2147483647 + 1.0)

returns 'Double' ( FLOAT ). 返回'Double'( FLOAT )。

No doubt there are other exceptions I haven't stumbled upon. 毫无疑问,还有其他例外我没有偶然发现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM