Bạn đã bao giờ nghĩ rằng SQL có thể sai trong toán học? Nghe có vẻ điên rồ. Nhưng nếu bạn đã sử dụng kiểu dữ liệu SQL FLOAT, bạn có thể gặp phải những vấn đề mà tôi sắp trình bày cho bạn.
Xem xét điều này. 0,1 + 0,2 phải là 0,3, phải không? Nhưng hãy kiểm tra điều này bằng cách sử dụng kiểu dữ liệu SQL FLOAT.
DECLARE @f1 FLOAT = 0.1
DECLARE @f2 FLOAT = 0.2
SELECT CASE WHEN @f1 + @f2 = .3 THEN 1 ELSE 0 END
Kết quả đúng là 1. Nhưng hãy kiểm tra Hình 1.
Bây giờ tôi có chú ý đến bạn không? Tôi chắc chắn hy vọng như vậy. Thật là đáng sợ khi phụ thuộc vào một hệ thống sẽ không cho chúng ta phép toán chính xác. Nhưng bài viết này sẽ giúp bạn tránh điều này.
Có một số việc phải làm. Chúng ta cần bắt đầu từ kiểu dữ liệu FLOAT.
Kiểu dữ liệu SQL FLOAT là gì?
Kiểu dữ liệu SQL FLOAT là kiểu dữ liệu số gần đúng được sử dụng cho các số dấu phẩy động. Họ có thể lưu trữ số lượng rất lớn hoặc rất nhỏ. Chúng cũng được sử dụng cho các tính toán yêu cầu thời gian xử lý nhanh.
Tất cả những điều này đều phải trả giá bằng việc mất độ chính xác. Hơn nữa, bạn không thể nói vị trí dấu thập phân sẽ được đặt sau khi tính toán - nó trôi nổi . Trong khi đó, các số chính xác như DECIMAL sẽ có vị trí dấu thập phân cố định.
Cách bạn khai báo kiểu dữ liệu SQL FLOAT
Cú pháp là FLOAT [(n)], trong đó n là số bit được sử dụng để lưu trữ phần định trị của một số dấu phẩy động trong ký hiệu khoa học. Điều đó cũng quyết định độ chính xác và kích thước lưu trữ. Các giá trị có thể có cho n nằm trong khoảng từ 1 đến 53. Lưu ý rằng n là tùy chọn.
Đây là một ví dụ:
DECLARE @floatValue1 FLOAT; -- Float variable without the number of bits
DECLARE @floatValue2 FLOAT(3) -- Float variable with 3 bits
Nếu bạn không chỉ định n , mặc định là 53. Đó cũng là giá trị lớn nhất. Hơn nữa, FLOAT (53) là số dấu phẩy động có độ chính xác kép hoặc số nhị phân64. Ngoài việc sử dụng FLOAT (53), bạn cũng có thể khai báo nó là CHÍNH XÁC ĐÔI.
3 khai báo sau là tương đương về chức năng:
DECLARE @double1 FLOAT(53);
DECLARE @double2 FLOAT;
DECLARE @double3 DOUBLE PRECISION;
Bảng hiển thị số lượng bit và kích thước lưu trữ tương ứng.
Giá trị của n | Kích thước bộ nhớ |
1 đến 24 | 4 byte |
25 đến 53 | 8 byte |
SQL FLOAT và REAL có giống nhau không?
REAL cũng là FLOAT (24). Nó còn được gọi là độ chính xác đơn hoặc nhị phân32.
Tại sao việc biết điều này lại quan trọng
Biết rằng đây là một số gần đúng sẽ khiến bạn không thể sử dụng nó cho các phép tính yêu cầu độ chính xác. Bạn cũng quan tâm đến lưu trữ và bộ nhớ? Sử dụng REAL hoặc FLOAT (24) nếu bạn không cần giá trị quá lớn hoặc quá nhỏ.
Sự khác biệt giữa FLOAT và DECIMAL là gì?
FLOAT là một số gần đúng. DECIMAL là một số chính xác. Dưới đây là tóm tắt về sự khác biệt trong Bảng:
NỔI | QUYẾT ĐỊNH | |
Dấu thập phân | Có thể được đặt ở bất kỳ đâu trong chữ số | Vị trí cố định |
Giới hạn tối đa | 38 chữ số hoặc 99.999.999.999.999.999.999.999.999.999.999.999.999 | FLOAT (53) có phạm vi tối đa là 1,79E + 308 hoặc 179 theo sau là 306 số 0 |
Bộ nhớ | Tối đa 8 byte | Tối đa 17 byte |
Kết quả tính toán | Gần đúng | Chính xác |
Kiểm tra so sánh | Không sử dụng =hoặc <>. Tránh khi làm tròn số | toán tử=hoặc <>. Tốt để làm tròn |
Bạn đã thấy trong Hình 1 cách tính toán số FLOAT có thể có kết quả kỳ lạ. Nếu bạn thay đổi kiểu dữ liệu thành DECIMAL như thế này:
DECLARE @d1 DECIMAL(2,1) = 0.1
DECLARE @d2 DECIMAL(2,1) = 0.2
SELECT CASE WHEN @d1 + @d2 = 0.3 THEN 1 ELSE 0 END
Kết quả sẽ chính xác.
Sử dụng một toán tử bất đẳng thức cũng là một vấn đề. Kiểm tra vòng lặp bên dưới.
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue <> 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
Bạn nghĩ sao? Xem Hình 2 bên dưới.
Bùm! Vòng lặp vô hạn! Điều kiện bất đẳng thức sẽ luôn đúng. Vì vậy, lựa chọn hợp lý là thay đổi loại thành DECIMAL.
DECLARE @decimalValue DECIMAL(2,1) = 0.0
WHILE @decimalValue <> 5.0
BEGIN
PRINT @decimalValue;
SET @decimalValue += 0.1;
END
Đoạn mã trên chắc chắn sẽ dừng lại khi @ decimalValue bằng 5,0. Hãy tự mình xem trong Hình 3 bên dưới.
Tốt đẹp! Nhưng nếu bạn vẫn nhấn mạnh vào FLOAT, điều này sẽ hoạt động tốt mà không cần vòng lặp vô hạn.
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue < 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
Trong khi đó, làm tròn số cũng bị tắt. Hãy xem xét những điều sau:
DECLARE @value FLOAT(2) = 1.15
SELECT ROUND(@value, 1) -- This will result to 1.1
Thay vì 1,20, kết quả mã là 1,1. Nhưng nếu bạn sử dụng DECIMAL, kết quả sẽ chính xác.
DECLARE @value DECIMAL(3,2) = 1.15
SELECT ROUND(@value, 1) -- This will result in 1.2 or 1.20
Khi FLOAT là đúng và DECIMAL thì không
Các con số chính xác luôn KHÔNG chính xác? Để tái tạo vấn đề này, chúng tôi sẽ sử dụng một phép tính, và sau đó chúng tôi đảo ngược nó. Trước tiên, hãy chuẩn bị dữ liệu.
CREATE TABLE ExactNumerics1
(
fixed1 DECIMAL(8,4),
fixed2 DECIMAL(8,4),
fixed3 DECIMAL(8,4),
calcValue1 AS fixed3 / fixed1 * fixed2
)
GO
INSERT INTO ExactNumerics1
(fixed1,fixed2,fixed3)
VALUES
(54,0.03,1*54/0.03)
Bảng trên sẽ sử dụng giá trị cố định cho 2 cột đầu tiên. Cột thứ ba sẽ có phép tính. Cuối cùng, cột thứ tư, là một cột được tính toán, sẽ thực hiện tính toán ngược lại. Kết quả đúng trong cột được tính phải là 1.
Bây giờ, để so sánh nó với FLOAT, hãy tạo một bảng và dữ liệu tương tự.
CREATE TABLE ApproxNumerics1
(
float1 FLOAT(2),
float2 FLOAT(2),
float3 FLOAT(2),
calcValue1 AS float3 / float1 * float2
)
INSERT INTO ApproxNumerics1
(float1, float2, float3)
VALUES
(54,0.03,1*54/0.03)
Hãy truy vấn.
SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1
Kết quả? Xem Hình 4.
Điều gì đã xảy ra ở đây? FLOAT hiểu đúng, nhưng DECIMAL thì không. Đã xảy ra sự cố.
CHUYỂN ĐỔI IMPLICIT CÓ LẠI KHÔNG
Chuyển đổi ngầm xảy ra bởi vì SQL được tha thứ. Khi các kiểu dữ liệu khác nhau được sử dụng trong một phép tính, SQL Server sẽ cố gắng chuyển đổi nó bằng cách sử dụng chuyển đổi ngầm sau lưng của chúng tôi.
Một chuyển đổi có thực sự xảy ra không? Bên cạnh đó, mọi cột trong ExactNumerics1 bảng là một DECIMAL.
Hãy kiểm tra cấu trúc bảng của ExactNumerics1 bảng trong SQL Server Management Studio:
Lưu ý vùng hộp màu đỏ trong Hình 3. Cột được tính là DECIMAL (30,17), không phải DECIMAL (8,4). Theo tài liệu chính thức, 2 cột DECIMAL có độ chính xác và tỷ lệ khác nhau là 2 loại dữ liệu khác nhau . Xem cho chính mình ở đây. Vì sự khác biệt, một chuyển đổi là bắt buộc. Vì vậy, chuyển đổi ngầm phát huy tác dụng.
Điều gì sẽ xảy ra nếu chúng khác nhau và một chuyển đổi ngầm đã xảy ra?
Một lần nữa, dựa trên tài liệu chính thức, việc mất độ chính xác hoặc tỷ lệ có thể xảy ra trong quá trình chuyển đổi ngầm định . Do đó, một CAST rõ ràng là bắt buộc. Lưu ý kiểu dữ liệu DECIMAL trong bảng chuyển đổi trong tham chiếu đó.
Một số mất mát vừa xảy ra ở đây. Nếu cột được tính toán cũng là DECIMAL (8,4), thì chuyển đổi ngầm định sẽ không xảy ra.
Để tránh chuyển đổi ngầm, hãy làm theo tài liệu chính thức. Cấu trúc bảng lẽ ra phải như thế này:
CREATE TABLE ExactNumerics2
(
fixed1 DECIMAL(8,4),
fixed2 DECIMAL(8,4),
fixed3 DECIMAL(8,4),
calcValue1 AS CAST(fixed3 / fixed1 * fixed2 AS DECIMAL(8,4)) -- the explicit CAST
)
CAST rõ ràng trong cột được tính toán đảm bảo rằng các kiểu dữ liệu nhất quán. Nếu chúng ta cũng theo cấu trúc này và chèn cùng một dữ liệu, kết quả sẽ đúng. Kiểm tra kết quả mới trong Hình 6 bên dưới.
Cuối cùng, các con số chính xác sẽ không chính xác nếu một chuyển đổi ngầm xảy ra giữa 2 hoặc nhiều giá trị DECIMAL.
Tại sao việc biết điều này lại quan trọng
Nó cung cấp cho bạn ý tưởng về những gì bạn cần cho các bảng và biến của mình. Hơn nữa, chuyển đổi ngầm có thể làm cho những con số chính xác thậm chí trở nên sai lầm. Vì vậy, hãy xác định rõ ràng độ chính xác và tỷ lệ và nhất quán với nó trong tính toán của bạn.
Tôi có nên sử dụng SQL FLOAT cho Dữ liệu tài chính không?
Khi tính toán tỷ lệ phần trăm trong mỗi lát của biểu đồ hình tròn, tổng phải là 100%. Tổng số trong báo cáo tóm tắt và báo cáo chi tiết cũng phải nhất quán. Nếu độ chính xác của kết quả là quan trọng, thì kiểu dữ liệu gần đúng như FLOAT sẽ không thực hiện được công việc. Sự lựa chọn hợp lý cho việc này là DECIMAL.
Nhưng một câu hỏi vẫn còn.
Khi nào bạn nên sử dụng FLOAT?
Sử dụng FLOAT cho dữ liệu yêu cầu các giá trị thiên văn như khoảng cách giữa các thiên hà. Trong khi đó, kiểu dữ liệu DECIMAL sẽ bị tràn số học với kiểu dữ liệu này. Các giá trị nhỏ như đường kính của hạt nhân nguyên tử cũng sẽ phù hợp khi sử dụng FLOAT. Dữ liệu khoa học và các giá trị khác không yêu cầu độ chính xác cũng có thể được hưởng lợi từ FLOAT.
Tại sao việc biết điều này lại quan trọng
Chúng tôi không nói rằng FLOAT là xấu và DECIMAL là tốt hoặc ngược lại. Biết các trường hợp sử dụng chính xác cho từng trường hợp sẽ mang lại cho bạn và người dùng của bạn kết quả như mong đợi. Và một lần nữa, bạn muốn người dùng của mình hài lòng, phải không?
Kết luận
Vào cuối ngày, tất cả chúng ta đều muốn làm công việc của mình và giỏi chúng. Toán học sẽ luôn là một phần trong công việc của chúng tôi. Và việc biết các kiểu dữ liệu số chính xác cũng sẽ giúp chúng ta giải quyết nó. Không khó nếu bạn biết mình đang làm gì.
Tôi hy vọng bài viết này đã giúp bạn tránh được những phép toán kỳ lạ trong SQL Server.
Bạn có điều gì khác để thêm không? Sau đó, hãy cho chúng tôi biết trong phần Nhận xét. Cũng chia sẻ điều này trên các nền tảng truyền thông xã hội yêu thích của bạn.