2012-08-05 6 views
5

Ich hatte ein Problem beim Einfügen des Werts 32767 in eine smallint Spalte in Postgres, die den Fehler smallint außerhalb des Bereichs ergeben würde. Das war seltsam, weil ich tun konnte:PostgreSQL - Smallint überfüllt, wenn Index für mehrere Spalten erstellt wird. Ist das ein Fehler?

SELECT 32767::int2; 

Was würde gut funktionieren. Nach ein wenig Haarziehen habe ich das schließlich auf einen Index der fraglichen Spalte zurückverfolgt. Erstens ist hier das Schema (Na ja, nicht wirklich, aber ich habe diese bis zu einem repro Fall vereinfacht):

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

ich nun folgende Index erstellen:

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

Als nächstes versuche ich zu erstellen die folgende Zeile:

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

ich den Fehler:

ERROR: smallint out of range SQL state: 22003

Es scheint, dass idx_test_totaltime einen maximalen Wert von int2 erwartet, obwohl der Index auf die Summe von zwei Smallints angewendet wird.

Ist das ein Postgres-Bug, oder fehlt mir etwas Einfaches? Gibt es eine Möglichkeit, um diese Einschränkung zu umgehen, oder müsste ich diese Spalten int4 machen und eine CHECK-Einschränkung verwenden, um jeden Wert auf 32767 zu begrenzen? Ich benutze Postgres 9.0.0 (Ja, ich muss upgraden!), Aber ich habe eine SQL Fiddle erstellt, die diesen Fehler am 9.1.4 zeigt.

Antwort

4

Ihr Problem ist, dass int2 + int2 ist eine andere int2 so der Ausdruck in Ihrem Index, (cooktime + preptime), Überlauf für (32767, 10). Sie können in den Indexausdruck mit ein bisschen Casting, um dieses Problem umgehen:

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

Sie nur eine der Abgüsse müssen aber mit beiden nicht schaden.

+0

Das funktioniert. Allerdings habe ich bemerkt, wenn Sie 'SELECT * FROM Test WHERE CookTime + PrepTime> 100 'dann wird es nicht den Index verwenden. Sie müssen stattdessen 'WHERE CookTime :: Int4 + PrepTime :: Int4> 100 angeben. Jetzt muss ich meinen Suchcode aktualisieren :) –

+1

@MikeChristensen: Das ist ein guter Platz auf dem Index nicht gewöhnungsbedürftig. Ich würde empfehlen, einfach das gesamte Geschäft "int2" zu vergessen und "int" für die Spalte mit entsprechenden CHECK-Einschränkungen zu verwenden. –

+1

Übergeben Sie das Ergebnis der Berechnung an ein INT2, nicht an die Spalten im Index. –

3

Warum verwenden Sie keine INTERVAL für ein Zeitintervall? Es ist eine perfekte Lösung für Ihr Problem.

+0

Das ist wahrscheinlich auch eine großartige Lösung. Diese Änderung jetzt vorzunehmen, könnte etwas unordentlich sein, aber ich könnte etwas experimentieren, um zu sehen, was dabei herauskommt. Die Lösung von Mu funktioniert jedoch auch einwandfrei und erfordert, dass ich minimalen Code aktualisiere. –