Question

Problème

72 tables enfants, chacun ayant un indice de l'année et un indice de la station, sont définis comme suit:

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

(contraintes de clés étrangères à ajouter plus tard.)

La requête suivante va épouvantablement lent en raison d'un scan de table:

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 morosité vient de cette partie de la requête:

  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

Cette partie de la requête correspond à une sélection de jours. Par exemple, si l'utilisateur souhaite consulter les données entre le 1er Juin et le 1er Juillet sur toutes les années pour lesquelles il existe des données, les matches de la clause ci-dessus contre seulement ces jours. Si l'utilisation veut examiner les données entre Décembre 22 et 22 Mars à nouveau pour toutes les années pour lesquelles il existe des données, les calcule clause ci-dessus que Mars 22 est l'année suivante de Décembre 22 et correspond donc à la date en conséquence:

Actuellement, les dates sont fixées comme 1er janvier au 31 déc mais seront paramétrés, comme indiqué ci-dessus.

Le HashAggregate des spectacles du plan un coût de 10006220141,11, qui est, je pense, du côté astronomiquement énorme.

Il y a un scan de table sur la table de mesure (lui-même ayant des données ni ni index) en cours d'exécution. Les agrégats de table 273 millions de lignes de ses tables d'enfant.

Question

Quelle est la bonne façon d'indexer les dates pour éviter les analyses de table complet?

Options J'ai examiné:

  • GIN
  • GiST
  • Réécrire la clause WHERE
  • séparé year_taken, month_taken et colonnes day_taken aux tables

Que pensez-vous?

Merci!

Était-ce utile?

La solution

Votre problème est que vous avez une clause where en fonction d'un calcul de la date. Il n'y a aucun moyen de la base de données peut utiliser un index si elle a besoin d'aller chercher chaque ligne et faire un calcul sur elle avant de savoir si la date correspondra.

Sauf si vous repassez qu'il soit sous la forme où la base de données a une plage fixe pour vérifier ce qui ne dépend pas des données, il est de vous récupérer devrez toujours analyser la table.

Autres conseils

Essayez quelque chose comme ceci:

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

Je pense que pour exécuter cette efficacité à travers ces partitions, j'ai votre application soit beaucoup plus intelligent sur les plages de dates. Avez-il générer une liste réelle des dates pour vérifier par partition, puis l'ont générer une requête avec une union entre les partitions. Il semble que votre ensemble de données est assez statique, donc un CLUSTER sur votre indice de date pourrait grandement améliorer les performances ainsi.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top