sql - 优化大型子表的日期查询 : GiST or GIN?

标签 sql postgresql date query-optimization full-table-scan

问题

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

这部分查询与选定的日期相匹配。例如,如果用户想要查看所有年份中 6 月 1 日至 7 月 1 日之间的数据,则上述子句仅匹配那些日子。如果用户想要查看 12 月 22 日和 3 月 22 日之间的数据,再次针对所有有数据的年份,上述子句计算 3 月 22 日在下一年的 12 月 22 日,因此相应地匹配日期:

目前日期固定为 1 月 1 日到 12 月 31 日,但将参数化,如上所示。

计划中的 HashAggregate 显示成本为 10006220141.11,我怀疑这是一个巨大的天文数字。

正在对测量表(本身既没有数据也没有索引)执行全表扫描。该表聚合了其子表中的 2.73 亿行。

问题

为日期编制索引以避免全表扫描的正确方法是什么?

我考虑过的选项:

  • Gin
  • 要点
  • 重写 WHERE 子句
  • 将 year_taken、month_taken 和 day_taken 列分开到表中

你有什么想法?

谢谢!

最佳答案

您的问题是您有一个取决于日期计算的 where 子句。如果数据库需要获取每一行并在知道日期是否匹配之前对其进行计算,则数据库无法使用索引。

除非您将其重写为数据库具有固定范围以检查哪个不依赖于要检索的数据的形式,否则您将始终必须扫描表格。

关于sql - 优化大型子表的日期查询 : GiST or GIN?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2871057/

相关文章:

postgresql - 在 PL/pgSQL 中声明行类型变量

iphone - 如何禁用 iPhone 上的自动日期链接?

sql - Apache Spark 中的授权

sql - 如何使sql搜索查询更强大?

sql - Derby DB SQL,从行号开始选择行

javascript - 在 Node.js 中向 javascript 日期添加分钟

Python 过滤掉日期范围之外的事件(过去一小时)

mysql - SQL ORDER BY - 为什么它在这里不起作用?

postgresql - 无法找到从未知到文本的转换函数

sql - 如何在 postgresql 选择查询中仅从 age() 函数中获取年份