2012-08-05 9 views
5

Wystąpił problem z wstawieniem wartości 32767 do kolumny smallint w Postgresie, co spowodowałoby błąd o numerze smallint poza zakresem. To było dziwne, ponieważ mogłem zrobić:PostgreSQL - Smallint przepełnia się podczas tworzenia indeksu na wielu kolumnach. Czy to błąd?

SELECT 32767::int2; 

Który działałby dobrze. Po przeciągnięciu trochę włosów, w końcu śledziłem to do indeksu na kolumnie, o której mowa. Po pierwsze, oto schemat (dobrze, nie bardzo, ale mam uproszczona to w dół do repro przypadku):

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

teraz utworzyć następujący wskaźnik:

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

Następnie staram się tworzyć się następujący wiersz:

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

pojawia się błąd:

ERROR: smallint out of range SQL state: 22003

Wygląda na to, że idx_test_totaltime spodziewa się maksymalnej wartości int2, mimo że indeks jest stosowany do sumy dwóch małych znaków.

Czy jest to błąd PostgreSzu, czy też brakuje mi czegoś prostego? Czy istnieje sposób obejścia tego ograniczenia , czy musiałbym utworzyć te kolumny int4 i użyć ograniczenia CHECK, aby ograniczyć każdą wartość do 32767? Używam PostgreSQL 9.0.0 (tak, potrzebuję aktualizacji!), Ale utworzyłem kod SQL Fiddle, który demonstruje ten błąd w wersji 9.1.4.

Odpowiedz

4

Twój problem polega na tym int2 + int2 jest kolejnym int2 więc wyrażenie w indeksie, (cooktime + preptime), przelewa do (32767, 10). Można obejść ten problem z odrobiną odlewania w wyrażeniu indeksu:

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

Wystarczy tylko jeden z odlewów ale Obiema nie boli.

+0

To działa. Zauważyłem jednak, że jeśli wybierzesz 'SELECT * FROM Test WHERE CookTime + PrepTime> 100', to nie użyje on indeksu. Musisz zamiast tego podać "WHERE CookTime :: Int4 + PrepTime :: Int4> 100". Teraz muszę zaktualizować mój kod wyszukiwania :) –

+1

@MikeChristensen: To nie jest dobre miejsce na indeksie. Polecam po prostu zapomnieć o wszystkim biznesie "int2" i użyć 'int' dla kolumny z odpowiednimi ograniczeniami CHECK. –

+1

Prześlij wynik obliczeń do INT2, a nie do kolumn w indeksie. –

3

Dlaczego nie używasz INTERVAL przez pewien okres czasu? To idealne rozwiązanie dla twojego problemu.

+0

To też prawdopodobnie świetne rozwiązanie. Dokonanie tej zmiany teraz może być nieco kłopotliwe, ale mógłbym zrobić kilka eksperymentów, aby zobaczyć, co będzie zaangażowane. Jednak jego rozwiązanie działa również doskonale i wymagało aktualizacji minimalnego kodu. –