要点:
1.postgresql分表需要使用触发器完成,分表后一个主表以及n个子表;
2.给某个字段,如(update_time)设置自动填充,仍需要创建触发器,但是不能光绑定主表,应该给每一个子表都绑定该触发器;
分表触发器代码
CREATE OR REPLACE FUNCTION "public"."auto_insert_sub_indicator_table"()
RETURNS "pg_catalog"."trigger" AS $BODY$
DECLARE
admission_date text ; -- 父表中用于分区的时间字段的名称(推送时间)
curMM varchar(6); -- 'YYYYMM'字串,用做分区子表的后缀
isExist boolean; -- 分区子表,是否已存在
startTime text;
endTime text;
strSQL text;
-- 如果表名使用很多,也可以声明一个变量表示字表名称
BEGIN
-- 调用前,必须首先初始化(时间字段名):admission_date [直接从调用参数中获取!!]
-- 没有显示的声明参数,使用TG_ARGV[0]获取参数
admission_date := TG_ARGV[0];
-- 判断对应分区表 是否已经存在?
EXECUTE 'SELECT $1.'||admission_date INTO strSQL USING NEW;
curMM := to_char( strSQL::timestamp , 'YYYYMM' );
select count(*) INTO isExist from pg_class where relname = (TG_TABLE_NAME||'_'||curMM);
-- 若不存在, 则插入前需先创建子分区
IF ( isExist = false ) THEN
-- 创建子分区表,写明约束。TG_TABLE_NAME为主表的名字,分表将继承主表的所有字段,但不会继承主键和索引等,需要手动创建。
startTime := left(curMM, 4)||'-'||right(curMM, 2)||'-01 00:00:00';
endTime := to_char( startTime::timestamp + interval '1 month', 'YYYY-MM-DD HH24:MI:SS');
strSQL := 'CREATE TABLE IF NOT EXISTS '||'"'||TG_TABLE_NAME||'_'||curMM||'"'||
' ( CHECK('||admission_date||'>='||''''|| startTime||'''' ||' AND '
||admission_date||'< '||''''|| endTime ||''''||' )
) INHERITS ('||'"'||TG_TABLE_NAME||'"'||') ;';
EXECUTE strSQL;
-- 创建主键,id主键只能保证单个表的唯一,多个子表可能会存在相同的主键。这里为子表创建复合主键
strSQL := 'ALTER TABLE '||'"'||TG_RELNAME||'_'||curMM||'"'||' ADD PRIMARY KEY("patient_id","visit_id","section_id","field_id") ';
EXECUTE strSQL;
-- 创建索引(使用分表的字段),可选。 TODO: 现在先只为业务数据日期时间添加索引,后面有需要再加
strSQL := 'CREATE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_patient_id'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("patient_id");';
EXECUTE strSQL;
strSQL := 'CREATE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_visit_id'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("visit_id");';
EXECUTE strSQL;
strSQL := 'CREATE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_field_id'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("field_id");';
EXECUTE strSQL;
strSQL := 'CREATE UNIQUE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_unique'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("patient_id","visit_id","section_id","field_id");';
EXECUTE strSQL;
END IF;
-- 插入数据到子分区(主键冲突时更新数据,否则新增数据)!
strSQL := 'INSERT INTO '||'"'||TG_TABLE_NAME||'_'||curMM||'"'|| ' SELECT $1.*' ||
' on conflict (patient_id, visit_id, section_id, field_id) ' ||
' do update set ' ||
' patient_id = EXCLUDED.patient_id,' ||
' visit_id = EXCLUDED.visit_id,' ||
' section_id = EXCLUDED.section_id,' ||
' field_id = EXCLUDED.field_id,' ||
' field_code = EXCLUDED.field_code,' ||
' field_value = EXCLUDED.field_value,' ||
' field_name = EXCLUDED.field_name,' ||
' update_time = EXCLUDED.update_time,' ||
' admission_date = EXCLUDED.admission_date' ;
EXECUTE strSQL USING NEW;
RETURN NULL;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
其中子表名是不断变化的,即:‘"’||TG_TABLE_NAME||‘_’||curMM||‘"’
只要把自动更新的触发器放到里面就可以了,每创建一个新子表,就为该子表绑定触发器。
CREATE OR REPLACE FUNCTION "public"."auto_insert_sub_indicator_table"()
RETURNS "pg_catalog"."trigger" AS $BODY$
DECLARE
admission_date text ; -- 父表中用于分区的时间字段的名称(推送时间)
curMM varchar(6); -- 'YYYYMM'字串,用做分区子表的后缀
isExist boolean; -- 分区子表,是否已存在
startTime text;
endTime text;
strSQL text;
-- 如果表名使用很多,也可以声明一个变量表示字表名称
BEGIN
-- 调用前,必须首先初始化(时间字段名):admission_date [直接从调用参数中获取!!]
-- 没有显示的声明参数,使用TG_ARGV[0]获取参数
admission_date := TG_ARGV[0];
-- 判断对应分区表 是否已经存在?
EXECUTE 'SELECT $1.'||admission_date INTO strSQL USING NEW;
curMM := to_char( strSQL::timestamp , 'YYYYMM' );
select count(*) INTO isExist from pg_class where relname = (TG_TABLE_NAME||'_'||curMM);
-- 若不存在, 则插入前需先创建子分区
IF ( isExist = false ) THEN
-- 创建子分区表,写明约束。TG_TABLE_NAME为主表的名字,分表将继承主表的所有字段,但不会继承主键和索引等,需要手动创建。
startTime := left(curMM, 4)||'-'||right(curMM, 2)||'-01 00:00:00';
endTime := to_char( startTime::timestamp + interval '1 month', 'YYYY-MM-DD HH24:MI:SS');
strSQL := 'CREATE TABLE IF NOT EXISTS '||'"'||TG_TABLE_NAME||'_'||curMM||'"'||
' ( CHECK('||admission_date||'>='||''''|| startTime||'''' ||' AND '
||admission_date||'< '||''''|| endTime ||''''||' )
) INHERITS ('||'"'||TG_TABLE_NAME||'"'||') ;';
EXECUTE strSQL;
-- *************************************
-- *************************************
-- *************************************
-- 为每个分表绑定触发器,自动填充update_time
strSQL := 'CREATE TRIGGER "auto_update_time" BEFORE UPDATE ON "public".'||'"'||TG_TABLE_NAME||'_'||curMM||'"'||
'FOR EACH ROW
EXECUTE PROCEDURE "public"."update_timestamp"();';
EXECUTE strSQL;
-- *************************************
-- *************************************
-- *************************************
-- 创建主键,id主键只能保证单个表的唯一,多个子表可能会存在相同的主键。这里为子表创建复合主键
strSQL := 'ALTER TABLE '||'"'||TG_RELNAME||'_'||curMM||'"'||' ADD PRIMARY KEY("patient_id","visit_id","section_id","field_id") ';
EXECUTE strSQL;
-- 创建索引(使用分表的字段),可选。 TODO: 现在先只为业务数据日期时间添加索引,后面有需要再加
strSQL := 'CREATE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_patient_id'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("patient_id");';
EXECUTE strSQL;
strSQL := 'CREATE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_visit_id'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("visit_id");';
EXECUTE strSQL;
strSQL := 'CREATE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_field_id'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("field_id");';
EXECUTE strSQL;
strSQL := 'CREATE UNIQUE INDEX '||'"'||TG_TABLE_NAME||'_'||curMM||'_INDEX_unique'||'"'||' ON '
||'"'||TG_TABLE_NAME||'_'||curMM||'"'||' ("patient_id","visit_id","section_id","field_id");';
EXECUTE strSQL;
END IF;
-- 插入数据到子分区(主键冲突时更新数据,否则新增数据)!
strSQL := 'INSERT INTO '||'"'||TG_TABLE_NAME||'_'||curMM||'"'|| ' SELECT $1.*' ||
' on conflict (patient_id, visit_id, section_id, field_id) ' ||
' do update set ' ||
' patient_id = EXCLUDED.patient_id,' ||
' visit_id = EXCLUDED.visit_id,' ||
' section_id = EXCLUDED.section_id,' ||
' field_id = EXCLUDED.field_id,' ||
' field_code = EXCLUDED.field_code,' ||
' field_value = EXCLUDED.field_value,' ||
' field_name = EXCLUDED.field_name,' ||
' update_time = EXCLUDED.update_time,' ||
' admission_date = EXCLUDED.admission_date' ;
EXECUTE strSQL USING NEW;
RETURN NULL;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100