2012-08-05 8 views
5

Estaba teniendo un problema al insertar el valor 32767 en una columna smallint en Postgres, lo que produciría el error smallint fuera del rango. Esto fue extraño porque pude hacer:PostgreSQL: desbordamiento de Smallint cuando se crea un índice en varias columnas. ¿Es esto un error?

SELECT 32767::int2; 

Que funcionaría bien. Después de tirar un poco de cabello, finalmente lo rastreé hasta un índice en la columna en cuestión. En primer lugar, aquí está el esquema (Bueno, no realmente, pero he simplificado esto a un caso repro):

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

ahora crear el siguiente índice:

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

A continuación, intento crear la fila siguiente:

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

me sale el error:

ERROR: smallint out of range SQL state: 22003

Parece que idx_test_totaltime espera un valor máximo de int2, aunque el índice se aplica sobre la suma de dos smallints.

¿Es esto un error de Postgres, o me estoy perdiendo algo simple? ¿Hay alguna forma de evitar esta limitación , o tendría que hacer estas columnas int4 y usar una restricción CHECK para limitar cada valor a 32767? Estoy usando Postgres 9.0.0 (Sí, necesito actualizar!) Pero he creado un SQL Fiddle que demuestra este error en 9.1.4.

Respuesta

4

Su problema es que es otra int2 + int2int2 por lo que la expresión en su índice, (cooktime + preptime), desbordamientos de (32767, 10). Puede solucionar este problema con un poco de calidad en la expresión de índice:

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

Sólo es necesario uno de los elencos, pero utilizando tanto no hace daño.

+0

Eso funciona. Sin embargo, me he dado cuenta si haces 'SELECT * FROM Test WHERE CookTime + PrepTime> 100', entonces no usará el índice. En su lugar, debe especificar 'WHERE CookTime :: Int4 + PrepTime :: Int4> 100'. Ahora tengo que actualizar mi código de búsqueda :) –

+1

@MikeChristensen: Es un buen lugar en el índice que no se usa. Yo recomendaría simplemente olvidarme de todo el negocio 'int2' y usar' int' para la columna con las restricciones CHECK apropiadas. –

+1

Transmite el resultado del cálculo a una INT2, no a las columnas del índice. –

3

¿Por qué no usa un INTERVAL por un intervalo de tiempo? Es una solución perfecta para tu problema.

+0

Esa es probablemente una gran solución también. Hacer este cambio ahora puede ser un poco desordenado, pero podría hacer algo de experimentación para ver qué implicaría. Sin embargo, la solución de Mu funciona perfectamente bien también y me exigió actualizar el código mínimo. –

Cuestiones relacionadas