Estoy realizando una actualización donde requiero una igualdad exacta en una tstzrange
variable. Se modifican ~ 1M filas, y la consulta tarda ~ 13 minutos. El resultado de EXPLAIN ANALYZE
se puede ver aquí , y los resultados reales son extremadamente diferentes de los estimados por el planificador de consultas. El problema es que la exploración de índice en t_range
espera que se devuelva una sola fila.
Esto parece estar relacionado con el hecho de que las estadísticas sobre los tipos de rango se almacenan de manera diferente a las de otros tipos. Mirando la pg_stats
vista de la columna, n_distinct
es -1 y otros campos (por ejemplo most_common_vals
, most_common_freqs
) están vacíos.
Sin embargo, debe haber estadísticas almacenadas en t_range
algún lugar. Una actualización extremadamente similar en la que utilizo un 'dentro' en t_range en lugar de una igualdad exacta tarda aproximadamente 4 minutos en realizarse, y utiliza un plan de consulta sustancialmente diferente (ver aquí ). El segundo plan de consulta tiene sentido para mí porque se usarán todas las filas de la tabla temporal y una fracción sustancial de la tabla de historial. Más importante aún, el planificador de consultas predice un número aproximadamente correcto de filas para el filtro t_range
.
La distribución de t_range
es un poco inusual. Estoy usando esta tabla para almacenar el estado histórico de otra tabla, y los cambios en la otra tabla ocurren todos a la vez en grandes volcados, por lo que no hay muchos valores distintos de t_range
. Aquí están los recuentos correspondientes a cada uno de los valores únicos de t_range
:
t_range | count
-------------------------------------------------------------------+---------
["2014-06-12 20:58:21.447478+00","2014-06-27 07:00:00+00") | 994676
["2014-06-12 20:58:21.447478+00","2014-08-01 01:22:14.621887+00") | 36791
["2014-06-27 07:00:00+00","2014-08-01 07:00:01+00") | 1000403
["2014-06-27 07:00:00+00",infinity) | 36791
["2014-08-01 07:00:01+00",infinity) | 999753
Los recuentos para los distintos t_range
anteriores están completos, por lo que la cardinalidad es de ~ 3 M (de los cuales ~ 1 M se verá afectado por cualquiera de las consultas de actualización).
¿Por qué la consulta 1 funciona mucho peor que la consulta 2? En mi caso, la consulta 2 es un buen sustituto, pero si realmente se requiere una igualdad de rango exacta, ¿cómo puedo hacer que Postgres use un plan de consulta más inteligente?
Definición de tabla con índices (descartar columnas irrelevantes):
Column | Type | Modifiers
---------------------+-----------+------------------------------------------------------------------------------
history_id | integer | not null default nextval('gtfs_stop_times_history_history_id_seq'::regclass)
t_range | tstzrange | not null
trip_id | text | not null
stop_sequence | integer | not null
shape_dist_traveled | real |
Indexes:
"gtfs_stop_times_history_pkey" PRIMARY KEY, btree (history_id)
"gtfs_stop_times_history_t_range" gist (t_range)
"gtfs_stop_times_history_trip_id" btree (trip_id)
Consulta 1:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range = '["2014-08-01 07:00:01+00",infinity)'::tstzrange;
Consulta 2:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND '2014-08-01 07:00:01+00'::timestamptz <@ sth.t_range;
Q1 actualiza 999753 filas y Q2 actualiza 999753 + 36791 = 1036544 (es decir, la tabla temporal es tal que cada fila que coincida con la condición de rango de tiempo se actualiza).
Intenté esta consulta en respuesta al comentario de @ ypercube :
Consulta 3:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range <@ '["2014-08-01 07:00:01+00",infinity)'::tstzrange
AND '["2014-08-01 07:00:01+00",infinity)'::tstzrange <@ sth.t_range;
El plan de consulta y los resultados (ver aquí ) fueron intermedios entre los dos casos anteriores (~ 6 minutos).
05/02/2016 EDITAR
Al no tener acceso a los datos después de 1,5 años, creé una tabla de prueba con la misma estructura (sin índices) y una cardinalidad similar. La respuesta de jjanes propuso que la causa podría ser el orden de la tabla temporal utilizada para la actualización. No pude probar la hipótesis directamente porque no tengo acceso track_io_timing
(usando Amazon RDS).
Los resultados generales fueron mucho más rápidos (por un factor de varios). Supongo que esto se debe a la eliminación de los índices, de acuerdo con la respuesta de Erwin .
En este caso de prueba, las consultas 1 y 2 básicamente tomaron la misma cantidad de tiempo, porque ambas usaron la combinación de combinación. Es decir, no pude activar lo que sea que estaba causando que Postgres eligiera la combinación hash, por lo que no tengo claro por qué Postgres estaba eligiendo la combinación hash de bajo rendimiento en primer lugar.
(lower(t_range),upper(t_range))
ya que verifica la igualdad.
(a = b)
en dos condiciones "contiene"(a @> b AND b @> a)
:? ¿Cambia el plan?