Thay vì can thiệp với câu trả lời của Martin hơn nữa, tôi sẽ thêm phần còn lại của những phát hiện của tôi POWER()
vào đây.
Giữ chặt lấy quần lót của bạn.
Lời nói đầu
Đầu tiên, tôi giới thiệu với bạn triển lãm A, tài liệu MSDN choPOWER()
:
Cú pháp
POWER ( float_expression , y )
Tranh luận
float_expression
Là một biểu thức của kiểu float hoặc của một loại có thể được chuyển đổi hoàn toàn thành float.
Các loại trả về
Tương tự như float_expression
.
Bạn có thể kết luận từ việc đọc dòng cuối cùng đó POWER()
là loại trả về FLOAT
, nhưng đọc lại. float_expression
là "thuộc loại float hoặc thuộc loại có thể được chuyển đổi hoàn toàn thành float". Vì vậy, mặc dù tên của nó, float_expression
thực sự có thể là a FLOAT
, a DECIMAL
hoặc an INT
. Vì đầu ra của POWER()
giống như của float_expression
nó, nó cũng có thể là một trong những loại đó.
Vì vậy, chúng ta có một hàm vô hướng với các kiểu trả về phụ thuộc vào đầu vào. Nó có thể là?
Quan sát
Tôi giới thiệu với bạn triển lãm B, một thử nghiệm chứng minh rằng POWER()
đưa đầu ra của nó sang các loại dữ liệu khác nhau tùy thuộc vào đầu vào của nó .
SELECT
POWER(10, 3) AS int
, POWER(1000000000000, 3) AS numeric0 -- one trillion
, POWER(10.0, 3) AS numeric1
, POWER(10.12305, 3) AS numeric5
, POWER(1e1, 3) AS float
INTO power_test;
EXECUTE sp_help power_test;
DROP TABLE power_test;
Các kết quả có liên quan là:
Column_name Type Length Prec Scale
-------------------------------------------------
int int 4 10 0
numeric0 numeric 17 38 0
numeric1 numeric 17 38 1
numeric5 numeric 17 38 5
float float 8 53 NULL
Có gì dường như xảy ra là POWER()
dàn diễn viên float_expression
vào loại nhỏ nhất mà phù hợp nó, không bao gồm BIGINT
.
Do đó, SELECT POWER(10.0, 38);
không thành công với lỗi tràn vì 10.0
được truyền tới mức NUMERIC(38, 1)
không đủ lớn để giữ kết quả của 10 38 . Đó là bởi vì 10 38 mở rộng để lấy 39 chữ số trước số thập phân, trong khi NUMERIC(38, 1)
có thể lưu trữ 37 chữ số trước số thập phân cộng với một chữ số sau nó. Do đó, giá trị tối đa NUMERIC(38, 1)
có thể giữ là 10 37 - 0,1.
Với sự hiểu biết này, tôi có thể tạo ra một lỗi tràn khác như sau.
SELECT POWER(1000000000, 3); -- one billion
Một tỷ (trái ngược với một nghìn tỷ từ ví dụ đầu tiên, được sử dụng NUMERIC(38, 0)
) chỉ đủ nhỏ để phù hợp với một INT
. Tuy nhiên, một tỷ được tăng lên sức mạnh thứ ba là quá lớn INT
, do đó lỗi tràn.
Một số chức năng khác thể hiện hành vi tương tự, trong đó loại đầu ra của chúng phụ thuộc vào đầu vào của chúng:
- Các hàm toán học :
POWER()
, CEILING()
, FLOOR()
, RADIANS()
, DEGREES()
, vàABS()
- Chức năng hệ thống và biểu thức :
NULLIF()
, ISNULL()
, COALESCE()
, IIF()
, CHOOSE()
, và CASE
biểu thức
- Toán tử số học : Cả hai
SELECT 2 * @MAX_INT;
và SELECT @MAX_SMALLINT + @MAX_SMALLINT;
, ví dụ, dẫn đến tràn số học khi các biến có kiểu dữ liệu được đặt tên.
Phần kết luận
Trong trường hợp cụ thể này, giải pháp là sử dụng SELECT POWER(1e1, precision)...
. Điều này sẽ làm việc cho tất cả các biện pháp có thể kể từ khi 1e1
được truyền tới FLOAT
, có thể chứa số lượng lớn một cách lố bịch .
Vì các chức năng này rất phổ biến, điều quan trọng là phải hiểu rằng kết quả của bạn có thể được làm tròn hoặc có thể gây ra lỗi tràn do hành vi của chúng. Nếu bạn mong đợi hoặc dựa vào một loại dữ liệu cụ thể cho đầu ra của mình, hãy bỏ rõ ràng đầu vào có liên quan nếu cần.
Vì vậy, những đứa trẻ, bây giờ bạn biết điều này, bạn có thể đi ra ngoài và thịnh vượng.