Frage

Problem

72 Kindertische, die jeweils ein Jahr Index und einen Stationsindex, sind wie folgt definiert:

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

(Foreign Key-Einschränkungen später hinzugefügt werden.)

Die folgende Abfrage läuft abysmally langsam aufgrund einer vollständigen Tabellenscan:

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 )

Die Flaute kommt aus diesem Teil der Abfrage:

  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

In diesem Teil der Abfrage entspricht eine Auswahl von Tagen. Zum Beispiel, wenn der Benutzer auf Daten vom 1. Juni bis 1. Juli über all die Jahre suchen will, für die es Daten, die oben genannten Klausel Begegnungen gegen nur jenen Tagen. Wenn die Verwendung will an Daten zwischen 22. Dezember und 22. März wieder für alle die Jahre suchen, für die Daten vorhanden sind, die oben genannten Klausel berechnet, dass 22. März im nächsten Jahr von 22. Dezember ist, und passt so das Datum entsprechend:

Zur Zeit werden die Daten als Jan fixiert Dezember 1-31, werden aber parametriert werden, wie oben gezeigt.

Die HashAggregate aus dem Plan zeigt eine Kosten für 10006220141,11, das ist, wie ich vermute, auf der astronomisch großen Seite.

Es ist ein Full-Table-Scan auf dem Messtisch (selbst weder Daten noch Indices) durchgeführt wird. Die Tabelle Aggregate 273 Millionen Zeilen aus den untergeordneten Tabellen.

Frage

Was ist der richtige Weg, die Daten zu indizieren vollständige Tabellenscans zu vermeiden?

Optionen Ich habe in Betracht gezogen:

  • GIN
  • GiST
  • Geben Sie die WHERE-Klausel
  • Separate year_taken, month_taken und day_taken Spalten zu den Tabellen

Was denken Sie?

Danke!

War es hilfreich?

Lösung

Ihr Problem ist, dass Sie eine where-Klausel haben, abhängig von einer Berechnung des Datums. Es gibt keine Möglichkeit, die Datenbank einen Index verwenden kann, wenn er jede Zeile holen muss und auf eine Berechnung zu tun, bevor man weiß, ob das Datum übereinstimmen.

Wenn Sie es umschreiben in Form sein, wo die Datenbank einen festen Bereich hat zu prüfen, welche nicht auf den Daten abhängt, es abzurufen ist, dass Sie immer die Tabelle scannen müssen.

Andere Tipps

Versuchen Sie etwas wie folgt aus:

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

ich denke, über diese Partitionen dies effizient laufen würde ich Ihre App über die Datumsbereiche viel schlauer. Haben sie eine aktuelle Liste der Daten generieren pro Partition zu überprüfen, und dann haben sie eine Abfrage mit einer UNION zwischen den Trennwänden zu erzeugen. Es klingt wie Ihr Datensatz ziemlich statisch ist, so ein CLUSTER auf Ihrem Datum Index die Leistung erheblich und verbessern könnte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top