Làm thế nào để SQL Server xác định độ chính xác / tỷ lệ?


8

Tôi đang chạy SQL Server 2012

SELECT 
   0.15 * 30 / 360,
   0.15 / 360 * 30 

Các kết quả:

 0.012500, 
 0.012480

Điều này thậm chí còn khó hiểu với tôi:

DECLARE @N INT = 360
DECLARE @I DECIMAL(38,26) = 0.15 * 30 / 360     
DECLARE @C DECIMAL(38,26) = 1000000     

SELECT @C *  @I *  POWER(1 + @I, @N)  / ( POWER(1 + @I, @N) - 1 )
SELECT @C * (@I *  POWER(1 + @I, @N)  / ( POWER(1 + @I, @N) - 1 ) )

Lựa chọn đầu tiên cho tôi kết quả chính xác: 12644.44022 Lựa chọn thứ hai cắt ngắn kết quả: 12644.00000


1
Xem câu trả lời này: stackoverflow.com/a/51445919/87015 , nó chứa một mã mẫu xác định loại dữ liệu dự kiến ​​dựa trên loại dữ liệu đầu vào và hoạt động.
Salman A

Câu trả lời:


13

Xác định độ chính xác và tỷ lệ kết quả từ các biểu thức là tổ của chuột và tôi không nghĩ có ai hiểu các quy tắc chính xác trong mọi kịch bản, đặc biệt là khi trộn số thập phân (hoặc dấu phẩy!) Và int. Xem câu trả lời này của gbn .

Tất nhiên bạn có thể điều chỉnh các biểu thức để cung cấp cho bạn những gì bạn muốn bằng cách thực hiện các chuyển đổi rõ ràng dài dòng hơn nhiều. Đây có thể là quá mức cần thiết nhưng:

SELECT 
   CONVERT(DECIMAL(15,6), CONVERT(DECIMAL(15,6), 0.15) 
   * CONVERT(DECIMAL(15,6), 30) 
   / CONVERT(DECIMAL(15,6), 360)),
   CONVERT(DECIMAL(15,6), CONVERT(DECIMAL(15,6), 0.15) 
   / CONVERT(DECIMAL(15,6), 360) 
   * CONVERT(DECIMAL(15,6), 30));

Không có kết quả nào được làm tròn sai do toán học dấu phẩy động bị hỏng hoặc độ chính xác / thang đo sai.

0.012500    0.012500

6

Như Aaron Bertrand đã đề cập, các biểu thức rất khó đoán.

Nếu bạn dám đến đó, bạn có thể cố gắng hiểu rõ hơn bằng cách sử dụng đoạn mã sau:

DECLARE @number SQL_VARIANT
SELECT @number = 0.15 / 360
SELECT @number
SELECT  
    SQL_VARIANT_PROPERTY(@number, 'BaseType') BaseType,
    SQL_VARIANT_PROPERTY(@number, 'MaxLength') MaxLength,
    SQL_VARIANT_PROPERTY(@number, 'Precision') Precision

Đây là kết quả:

------------
0.000416

(1 row(s) affected)

BaseType     MaxLength    Precision
------------ ------------ ----------
numeric      5            6

(1 row(s) affected)

3

Mặc dù các câu trả lời xuất sắc đã được thêm vào câu hỏi này, có một thứ tự ưu tiên được xác định rõ ràng để chuyển đổi các loại dữ liệu trong SQL Server.

Khi một toán tử kết hợp hai biểu thức của các loại dữ liệu khác nhau, các quy tắc về quyền ưu tiên loại dữ liệu xác định rằng loại dữ liệu có mức độ ưu tiên thấp hơn được chuyển đổi thành loại dữ liệu có độ ưu tiên cao hơn. Nếu chuyển đổi không phải là chuyển đổi ngầm được hỗ trợ, lỗi sẽ được trả về. Khi cả hai biểu thức toán hạng có cùng kiểu dữ liệu, kết quả của phép toán có kiểu dữ liệu đó.

SQL Server sử dụng thứ tự ưu tiên sau cho các loại dữ liệu:

user-defined data types (highest)
sql_variant
xml
datetimeoffset
datetime2
datetime
smalldatetime
date
time
float
real
decimal
money
smallmoney
bigint
int
smallint
tinyint
bit
ntext
text
image
timestamp
uniqueidentifier
nvarchar (including nvarchar(max) )
nchar
varchar (including varchar(max) )
char
varbinary (including varbinary(max) )
binary (lowest)

Vì vậy, ví dụ, nếu bạn SELECT 0.5 * 1(nhân một số thập phân với một số nguyên), bạn sẽ nhận được một kết quả được chuyển đổi thành giá trị thập phân, vì decimalnó có độ ưu tiên cao hơn intloại dữ liệu.

Xem http://msdn.microsoft.com/en-us/l Library / ms190309.aspx để biết thêm chi tiết.

Đã nói tất cả, có lẽ SELECT @C * (@I * POWER(1 + @I, @N) / (POWER(1 + @I, @N) - 1 )); nên trả về một giá trị thập phân, vì thực tế tất cả các đầu vào là thập phân. Thật thú vị, bạn có thể buộc một kết quả chính xác bằng cách sửa đổi nó SELECTthành:

DECLARE @N INT = 360;
DECLARE @I DECIMAL(38,26) = 0.15 * 30 / 360;
DECLARE @C DECIMAL(38,26) = 1000000;

SELECT @C *  @I *  POWER(1 + @I, @N)  / (POWER(1 + @I, @N) - 1);
SELECT @C * (@I *  POWER(1 + @I, @N)  / (POWER(1E0 + @I, @N) - 1));

Điều này trả về:

nhập mô tả hình ảnh ở đây

Tôi không thể giải thích được điều đó tạo ra sự khác biệt như thế nào, mặc dù rõ ràng là có . Tôi đoán1E0(một dấu phẩy rõ ràng) trong POWER(hàm buộc SQL Server phải đưa ra lựa chọn khác về các loại đầu ra cho POWERhàm. Nếu giả sử của tôi là chính xác, điều đó sẽ chỉ ra một lỗi có thể xảy ra trong POWERhàm, vì tài liệu nêu đầu vào đầu tiên POWER()là float hoặc một số có thể được chuyển đổi hoàn toàn thành float.


2
Một liên kết đến Chính xác, Tỷ lệ và Độ dài cũng thích hợp.
ypercubeᵀᴹ

2

Bạn có quen thuộc với SELECT .. INTO cú pháp ? Đây là một mẹo hữu ích để giải mã các tình huống như thế này vì nó tạo ra một bảng nhanh chóng chỉ với các loại dữ liệu phù hợp cho SELECTdanh sách đã cho .

Bạn có thể chia tính toán của mình thành các bước cấu thành của nó, áp dụng các quy tắc ưu tiên của Máy chủ SQL khi bạn đi, để xem định nghĩa thay đổi như thế nào. Đây là ví dụ đầu tiên của bạn sẽ như thế nào:

use tempdb;

SELECT
   0.15             as a,
   0.15 * 30        as b,
   0.15 * 30 / 360  as c
into #Step1;

select * from #Step1;

select
    c.name,
    t.name,
    c.precision,
    c.scale
from sys.columns as c
inner join sys.types as t
    on t.system_type_id = c.system_type_id
where object_id = object_id('#Step1');

drop table #Step1;

Đây là đầu ra:

name    name        precision   scale
a       numeric     2           2
b       numeric     5           2
c       numeric     9           6

1
Điều này không phải lúc nào cũng giúp. Cả hai (1+1)2đều thuộc loại intnhưng câu hỏi này có một ví dụ về việc cuối cùng họ tạo ra một kết quả đánh máy khác nhau.
Martin Smith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.