约束排除不适用于分区表

Posted

技术标签:

【中文标题】约束排除不适用于分区表【英文标题】:Constraint Exclusion not working on partitioned table 【发布时间】:2018-04-20 10:20:30 【问题描述】:

我有以下设置(简化摘录):

CREATE TABLE my_table (
  ID VARCHAR(255) NOT NULL,
  TENANT_ID CHAR(36) NOT NULL,
  PRIMARY KEY (id)
);

-- creates a int hash of a string (> 0)
CREATE OR REPLACE FUNCTION h_int(text) RETURNS int as $$
  SELECT @('x'||substr(md5($1),1,8))::bit(32)::int;
$$ language sql;

-- uses a numeric hash value to select a partition
CREATE OR REPLACE FUNCTION select_partition(text, int) RETURNS int as $$
  SELECT h_int($1) % $2;
$$ language sql;

CREATE TABLE IF NOT EXISTS part_my_table_00 (
  CONSTRAINT pk_part_00 PRIMARY KEY (ID),
  CONSTRAINT ck_part_00 CHECK (select_partition(TENANT_ID, 2) = 0)
) INHERITS (my_table);

CREATE INDEX idx_tenant_ids_00 ON my_table (TENANT_ID);

CREATE TABLE IF NOT EXISTS part_my_table_01 (
  CONSTRAINT pk_part_01 PRIMARY KEY (ID),
  CONSTRAINT ck_part_01 CHECK (select_partition(TENANT_ID, 2) = 1)
) INHERITS (my_table);

CREATE INDEX idx_tenant_ids_01 ON my_table (TENANT_ID);

CREATE OR REPLACE FUNCTION fn_insert() RETURNS TRIGGER AS $$
  declare
    selectedPartition int;
    tableName text := 'part_my_table_0';
    queryString text;
  BEGIN
    selectedPartition := select_partition(NEW.TENANT_ID, 2);
    tableName := tableName||selectedPartition;
    queryString := 'INSERT INTO '||tableName||' SELECT($1).*';
    EXECUTE queryString USING NEW;
    RETURN NULL;
  END;
  $$ LANGUAGE plpgsql;

CREATE TRIGGER tr_insert BEFORE INSERT ON my_table
FOR EACH ROW EXECUTE PROCEDURE fn_insert();

SET constraint_exclusion = on;

现在插入效果很好。我的问题是约束排除似乎不适用于选择:

SELECT * FROM my_table WHERE TENANT_ID='anyVal';

解释此查询返回以下内容:

postgres=# EXPLAIN SELECT * FROM my_table WHERE TENANT_ID='anyVal';
                                               QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..81.53 rows=11 width=6401)
   ->  Seq Scan on my_table  (cost=0.00..0.00 rows=1 width=6401)
         Filter: (tenant_id = 'bla'::bpchar)
   ->  Index Scan using idx_tenant_ids_00 on part_my_table_00 (cost=0.14..8.15 rows=1 width=6401)
         Index Cond: (tenant_id = 'bla'::bpchar)
   ->  Index Scan using idx_tenant_ids_01 on part_my_table_01 (cost=0.14..8.15 rows=1 width=6401)
         Index Cond: (tenant_id = 'bla'::bpchar)

我原以为只会扫描其中一个分区。有谁能帮帮我吗?

提前致谢!

【问题讨论】:

检查约束中的表达式与 where 子句中的表达式不匹配。 如何通过计算WHERE tenant_id=** 值的哈希来实现约束排除? 【参考方案1】:

您必须添加与分区约束匹配的WHERE 条件:

SELECT * FROM my_table
   WHERE TENANT_ID='anyVal'
     AND select_partition(TENANT_ID, 2) = select_partition('anyVal', 2);

此外,我认为您应该将h_intselect_partition 标记为IMMUTABLE。它们是(并且应该是),告诉数据库是个好主意。但这与您的问题无关。

【讨论】:

谢谢,解释清楚了!

以上是关于约束排除不适用于分区表的主要内容,如果未能解决你的问题,请参考以下文章

如何从分析查询中排除选择分区?

pg添加分区失败

SQL调优指南笔记5:Query Transformations

插入触发器最终在分区表中插入重复的行

从 SQL 窗口函数中排除分区?

Oracle SQL - 对 ID 求和分区但排除重复