Оптимизация даты запроса для крупных детских столов: Gist или Gin?

StackOverflow https://stackoverflow.com/questions/2871057

Вопрос

Проблема

72 Детские столы, каждый из которых имеет индекс года и индекс станции, определяются следующим образом:

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

(Ограничения ключей иностранных ключей будут добавлены позже.)

Следующий запрос проходит удачливо медленно из-за полной таблицы сканирования:

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 )

Глазность исходит из этой части запроса:

  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

Эта часть запроса соответствует выбору дней. Например, если пользователь хочет посмотреть на данные с 1 июня и 1 июля в течение всех лет, на которые есть данные, вышеупомянутое предложение соответствует только те дни. Если использование хочет посмотреть на данные с 22 декабря и 22 марта, снова за все годы, для которых есть данные, вышеуказанное предложение рассчитывает, что 22 марта находится в следующем году 22 декабря, и поэтому соответствует дату соответственно:

В настоящее время даты фиксируются как 1 января по 31 января, но будут параметризованы, как показано выше.

Hashaggregate из плана показывает стоимость 10006220141.11, которая есть, я подозреваю на астрономической огромной стороне.

На таблице измерений есть полное сканирование таблицы (сама не имеющая ни данных, ни индексов). Стол агрегат 273 миллиона строк из его детских столов.

Вопрос

Какой правильный способ индексировать даты, чтобы избежать полной таблицы сканирования?

Варианты я рассмотрел:

  • ДЖИН
  • Суть
  • Переписать предложение где
  • Отдельный год, MIX_TAKEN и DAY_TAKEN CONCOLS на таблицы

о чем ты думаешь?

Спасибо!

Это было полезно?

Решение

Ваша проблема заключается в том, что у вас есть предложение там в зависимости от расчета даты. В базе отсутствуют база данных, который может использовать индекс, если ему необходимо получить каждую строку и сделать расчет на нем, прежде чем знать, если дата будет соответствовать.

Если вы не переписываете его в форме, когда база данных имеет фиксированный диапазон для проверки, который не зависит от данных, чтобы получить, что вы всегда должны будут сканировать таблицу.

Другие советы

Попробуйте что-то вроде этого:

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

Я думаю, что это эффективно бежать на этих разделах, у меня было бы, чтобы ваше приложение было умного умнее о диапазонах даты. Создайте ли он фактический список дат для проверки на раздел, а затем оно порождает один запрос союзом между разделами. Похоже, ваш набор данных довольно статический, поэтому кластер на вашем индексе даты также может значительно повысить производительность.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top