Pregunta

Problema:

72 tablas secundarias, que tienen cada uno un índice de años y un índice de estación, se definen como sigue:

CREATE TABLE climate.measurement_12_013
(
-- Inherited from table climate.measurement_12_013:  id bigint NOT NULL DEFAULT nextval('climate.measurement_id_seq'::regclass),
-- Inherited from table climate.measurement_12_013:  station_id integer NOT NULL,
-- Inherited from table climate.measurement_12_013:  taken date NOT NULL,
-- Inherited from table climate.measurement_12_013:  amount numeric(8,2) NOT NULL,
-- Inherited from table climate.measurement_12_013:  category_id smallint NOT NULL,
-- Inherited from table climate.measurement_12_013:  flag character varying(1) NOT NULL DEFAULT ' '::character varying,
  CONSTRAINT measurement_12_013_category_id_check CHECK (category_id = 7),
  CONSTRAINT measurement_12_013_taken_check CHECK (date_part('month'::text, taken)::integer = 12)
)
INHERITS (climate.measurement)

CREATE INDEX measurement_12_013_s_idx
  ON climate.measurement_12_013
  USING btree
  (station_id);
CREATE INDEX measurement_12_013_y_idx
  ON climate.measurement_12_013
  USING btree
  (date_part('year'::text, taken));

(restricciones de clave externa que se añadirán más tarde.)

La siguiente consulta se ejecuta abismalmente lento debido a un escaneo completo de tabla:

SELECT
  count(1) AS measurements,
  avg(m.amount) AS amount
FROM
  climate.measurement m
WHERE
  m.station_id IN (
    SELECT
      s.id
    FROM
      climate.station s,
      climate.city c
    WHERE
        /* For one city... */
        c.id = 5182 AND

        /* Where stations are within an elevation range... */
        s.elevation BETWEEN 0 AND 3000 AND

        /* and within a specific radius... */
        6371.009 * SQRT( 
          POW(RADIANS(c.latitude_decimal - s.latitude_decimal), 2) +
            (COS(RADIANS(c.latitude_decimal + s.latitude_decimal) / 2) *
              POW(RADIANS(c.longitude_decimal - s.longitude_decimal), 2))
        ) <= 50
    ) AND

  /* Data before 1900 is shaky; insufficient after 2009. */
  extract( YEAR FROM m.taken ) BETWEEN 1900 AND 2009 AND

  /* Whittled down by category... */
  m.category_id = 1 AND

  /* Between the selected days and years... */
  m.taken BETWEEN
   /* Start date. */
   (extract( YEAR FROM m.taken )||'-01-01')::date AND
    /* End date. Calculated by checking to see if the end date wraps
       into the next year. If it does, then add 1 to the current year.
    */
    (cast(extract( YEAR FROM m.taken ) + greatest( -1 *
      sign(
        (extract( YEAR FROM m.taken )||'-12-31')::date -
        (extract( YEAR FROM m.taken )||'-01-01')::date ), 0
    ) AS text)||'-12-31')::date
GROUP BY
  extract( YEAR FROM m.taken )

La lentitud proviene de esta parte de la consulta:

  m.taken BETWEEN
    /* Start date. */
  (extract( YEAR FROM m.taken )||'-01-01')::date AND
    /* End date. Calculated by checking to see if the end date wraps
      into the next year. If it does, then add 1 to the current year.
    */
    (cast(extract( YEAR FROM m.taken ) + greatest( -1 *
      sign(
        (extract( YEAR FROM m.taken )||'-12-31')::date -
        (extract( YEAR FROM m.taken )||'-01-01')::date ), 0
    ) AS text)||'-12-31')::date

Esta parte de la consulta coincide con una selección de días. Por ejemplo, si el usuario quiere mirar los datos entre el 1 de junio y 1 de julio por todos los años para los que hay datos, los anteriores partidos contra cláusula sólo esos días. Si el uso quiere mirar los datos entre el 22 de diciembre y el 22 de marzo de nuevo por todos los años para los que hay datos, los anteriores calcula cláusula aquel mes de marzo 22a está en el año siguiente del 22 de diciembre, por lo que coincide con la fecha en consecuencia:

Actualmente las fechas son fijas como el 1 Ene a Dic 31, pero se pueden parametrizar, como se muestra arriba.

El HashAggregate de los espectáculos del plan un costo de 10006220141,11, que es, sospecho, en el lado astronómicamente enorme.

Hay un escaneo completo de tabla en la tabla de la medida (en sí misma teniendo ni datos ni índices) que se realiza. Los agregados de mesa 273 millones de filas de sus tablas secundarias.

Pregunta

¿Cuál es la forma correcta para indexar las fechas para evitar escaneos completos de tabla?

Opciones he considerado:

  • GIN
  • GiST
  • Vuelva a escribir la cláusula WHERE
  • year_taken independiente, month_taken y columnas day_taken a las tablas

¿Cuáles son sus pensamientos?

Gracias!

¿Fue útil?

Solución

Su problema es que usted tiene una cláusula en función de un cálculo de la fecha. No hay manera de la base de datos puede utilizar un índice si se necesita para traer cada fila y hacer un cálculo sobre el mismo antes de saber si la fecha coincidirá.

A menos que volver a escribir que sea en la forma en que la base de datos tiene un rango fijo para comprobar que no depende de los datos que se va a recuperar siempre tendrá explorar la tabla.

Otros consejos

intentar algo como esto:

create temporary table test (d date);

insert into test select '1970-01-01'::date+generate_series(1,50*365);

analyze test

create function month_day(d date) returns int as $$
  select extract(month from $1)::int*100+extract(day from $1)::int $$
language sql immutable strict;

create index test_d_month_day_idx on test (month_day(d));

explain analyze select * from test
  where month_day(d)>=month_day('2000-04-01')
  and month_day(d)<=month_day('2000-04-05');

Pienso para ejecutar esta manera eficiente a través de esas particiones tendría su aplicación sea mucho más inteligentes acerca de los intervalos de fechas. Tiene que generar una lista real de las fechas para ver por partición y luego tener que generar una consulta con una unión entre las particiones. Parece que su conjunto de datos es bastante estática, por lo que un clúster en el índice de fechas podría mejorar en gran medida el rendimiento también.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top