Domanda

problema

72 tabelle figlio, ciascuno con un indice di anno e un indice della stazione, sono definiti come segue:

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));

(vincoli di chiave da aggiungere in seguito.)

La seguente query viene eseguita abissalmente lento a causa di una scansione completa della tabella:

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 lentezza viene da questa parte della query:

  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

Questa parte della query corrisponde a una selezione di giorni. Ad esempio, se l'utente vuole guardare i dati tra il 1 ° giugno e il 1 luglio nel corso di tutti gli anni per i quali ci sono dati, le partite di cui sopra clausola contro solo quei giorni. Se l'uso vuole guardare i dati tra il 22 dicembre e il 22 marzo, ancora una volta per tutti gli anni per i quali non ci sono dati, le calcola clausola di cui sopra che 22 marzo è l'anno del 22 dicembre seguente, e così corrisponda alla data di conseguenza:

Al momento le date sono fissate 1 Gennaio-31 Dicembre, ma saranno parametrizzati, come indicato sopra.

Il HashAggregate dagli show piano di un costo di 10006220141,11, che è, ho il sospetto, sul lato astronomicamente enorme.

C'è una scansione completa della tabella sul tavolo misurazione (a dover né dati né indici) essendo eseguita. Gli aggregati tavolo 273 milioni di righe dalle sue tabelle figlio.

Domanda

Qual è il modo corretto per indicizzare le date per evitare la scansione completa della tabella?

Opzioni ho preso in considerazione:

  • GIN
  • GiST
  • Riscrivere la clausola WHERE
  • year_taken separata, month_taken, e le colonne day_taken alle tabelle

Quali sono i tuoi pensieri?

Grazie!

È stato utile?

Soluzione

Il tuo problema è che avete una clausola in cui a seconda di un calcolo della data. Non c'è modo il database può utilizzare un indice, se ha bisogno di recuperare ogni riga e fare un calcolo su di esso prima di sapere se la data corrisponderà.

A meno che non si riscrive di essere nella forma in cui il database ha un campo fisso per controllare che non dipende dai dati che è quello di recuperare avrete sempre la scansione della tabella.

Altri suggerimenti

provare qualcosa di simile:

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');

Credo che per eseguire in modo efficiente attraverso queste partizioni avrei la vostra applicazione sarà un sacco più intelligente intervalli di date. Averlo generare una lista reale di date per verificare per partizione e poi lo hanno generare una query con un'unione tra le partizioni. Sembra che il tuo set di dati è piuttosto statica, quindi un cluster su l'indice data potrebbe migliorare notevolmente le prestazioni pure.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top