2012-08-05 28 views
5

Tôi gặp sự cố khi chèn giá trị 32767 vào cột smallint trong Postgres, sẽ mang lại lỗi smallint ngoài phạm vi. Điều này thật kỳ quặc bởi vì tôi có thể làm:PostgreSQL - Smallint tràn khi tạo chỉ mục trên nhiều cột. Đây có phải là một lỗi?

SELECT 32767::int2; 

Điều gì sẽ hoạt động tốt. Sau khi kéo một chút tóc, cuối cùng tôi đã theo dõi điều này xuống một chỉ mục trên cột đang được đề cập đến. Thứ nhất, đây là giản đồ (Vâng, không thực sự nhưng tôi đã đơn giản hóa này xuống đến một trường hợp repro):

CREATE TABLE Test 
(
    id uuid NOT NULL, 
    cooktime smallint, 
    preptime smallint, 
    CONSTRAINT test_pkey PRIMARY KEY (id) 
) 
WITH (
    OIDS=FALSE 
); 

bây giờ tôi tạo ra các chỉ số sau:

CREATE INDEX idx_test_totaltime 
    ON Test 
    USING btree 
    ((cooktime + preptime)); 

Tiếp theo, tôi cố gắng tạo ra hàng sau:

INSERT INTO Test (CookTime, PrepTime, Id) 
VALUES (
    (32767)::int2, 
    (10)::int2, 
    (E'fd47dc1e-c3c6-42c1-b058-689e926a72a4')::uuid 
); 

tôi nhận được lỗi:

ERROR: smallint out of range SQL state: 22003

Có vẻ như idx_test_totaltime đang mong đợi giá trị tối đa là int2, mặc dù chỉ mục được áp dụng trên tổng hai smallints.

Đây có phải là lỗi Postgres hay tôi thiếu thứ gì đó đơn giản? Có cách nào để giải quyết giới hạn này hay tôi cần phải thực hiện các cột này int4 và sử dụng ràng buộc CHECK để giới hạn mỗi giá trị thành 32767? Tôi đang sử dụng Postgres 9.0.0 (Có, tôi cần phải nâng cấp!) Nhưng tôi đã tạo ra một SQL Fiddle mà chứng tỏ lỗi này trên 9.1.4.

Trả lời

4

Vấn đề của bạn là int2 + int2 là một int2 để biểu thức trong chỉ mục của bạn, (cooktime + preptime), tràn cho (32767, 10). Bạn có thể giải quyết vấn đề này với một chút đúc trong biểu thức chỉ mục:

CREATE INDEX idx_test_totaltime 
    ON Test 
    USING btree 
    ((cooktime::int4 + preptime::int4)); 

Bạn chỉ cần một trong các phôi nhưng sử dụng cả hai đều không bị tổn thương.

+0

Điều đó có hiệu quả. Tuy nhiên, tôi đã nhận thấy nếu bạn làm 'SELECT * FROM Test WHERE CookTime + PrepTime> 100' thì nó sẽ không sử dụng chỉ mục. Bạn phải chỉ định 'WHERE CookTime :: Int4 + PrepTime :: Int4> 100'. Bây giờ tôi phải cập nhật mã tìm kiếm của tôi :) –

+1

@MikeChristensen: Đó là một điểm tốt trên chỉ mục không được sử dụng. Tôi khuyên bạn nên quên tất cả các hoạt động 'int2' và sử dụng' int' cho cột với các ràng buộc CHECK thích hợp. –

+1

Truyền kết quả tính toán thành INT2, không phải các cột trong chỉ mục. –

3

Tại sao bạn không sử dụng INTERVAL trong một khoảng thời gian? Đó là một giải pháp hoàn hảo cho vấn đề của bạn.

+0

Đó có lẽ là một giải pháp tuyệt vời. Thực hiện thay đổi này bây giờ có thể hơi lộn xộn, nhưng tôi có thể làm một số thử nghiệm để xem những gì sẽ được tham gia. Tuy nhiên, giải pháp của Mu hoạt động hoàn hảo tốt và yêu cầu tôi cập nhật mã tối thiểu. –

Các vấn đề liên quan