aka*_*xer 6 postgresql partitioning identity postgresql-11
PostgreSQL 11
为分区表上的标识列生成默认值的最佳方法是什么。
例如
CREATE TABLE data.log
(
id BIGINT GENERATED ALWAYS AS IDENTITY
(
INCREMENT BY 1
MINVALUE -9223372036854775808
MAXVALUE 9223372036854775807
START WITH -9223372036854775808
RESTART WITH -9223372036854775808
CYCLE
),
epoch_millis BIGINT NOT NULL,
message TEXT NOT NULL
) PARTITION BY RANGE (epoch_millis);
CREATE TABLE data.foo_log
PARTITION OF data.log
(
PRIMARY KEY (id)
)
FOR VALUES FROM (0) TO (9999999999);
Run Code Online (Sandbox Code Playgroud)
如果我做:
INSERT INTO data.foo_log (epoch_millis, message)
VALUES (1000000, 'hello');
Run Code Online (Sandbox Code Playgroud)
我得到:
错误:“id”列中的空值违反了非空约束
详细信息:失败的行包含(null,1000000,hello)。
SQL状态:23502
因为默认生成的值不会应用于分区,除非我将其插入到根表中,如下所示:
INSERT INTO data.log (epoch_millis, message)
VALUES (1000000, 'hello');
Run Code Online (Sandbox Code Playgroud)
有时,出于性能原因(例如进行批量复制),我想直接插入特定分区。
我可以让它工作的唯一方法是创建分区,同时了解为标识列隐式创建的序列,如下所示:
CREATE TABLE data.foo_log
PARTITION OF data.log
(
id DEFAULT nextval('data.log_id_seq'),
PRIMARY KEY (id)
)
FOR VALUES FROM (0) TO (9999999999);
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来做到这一点,如果有的话怎么做?
一般来说,我不知道有更好的解决方案。不过,有一些小事情:
pg_get_serial_sequence()如果您不知道父级隐式序列的名称,请使用pg_get_serial_sequence()。
SELECT pg_get_serial_sequence('data.log', 'id');
Run Code Online (Sandbox Code Playgroud)
您甚至可以直接在脚本中使用表达式CREATE TABLE,但这会产生非常小的额外成本来计算默认值的实际名称(我认为每个事务一次),并且因为这是关于性能优化的......
COPY覆盖GENERATED ALWAYS,但触发器不覆盖id将列定义为GENERATED ALWAYS AS IDENTITY的效果是不允许您id在INSERT语句中为该列提供用户值,除非添加“覆盖”子句,例如:
INSERT INTO data.log (epoch_millis, message)覆盖用户值 VALUES (1000000, 'hello');
OVERRIDING USER VALUE如果指定此子句,则忽略为标识列提供的任何值,并应用默认序列生成的值。
例如,在表之间复制值时,此子句很有用。写入
INSERT INTO tbl2 OVERRIDING USER VALUE SELECT * FROM tbl1将从tbl1所有不是标识列的列进行复制,tbl2而标识列的值tbl2将由与 关联的序列生成tbl2。
COPY在任何情况下都会覆盖。手册:
对于标识列,该
COPY FROM命令将始终写入输入数据中提供的列值,如INSERT选项OVERRIDING SYSTEM VALUE。
但是,当使用您的解决方案直接写入分区时,INSERT也会覆盖,因此您有责任避免id直接为列提供用户值。另一种方法是使用触发器而不是分区中的默认值:
CREATE OR REPLACE FUNCTION trg_log_default_id()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
NEW.id := nextval('data.log_id_seq')
RETURN NEW;
END
$func$;
CREATE TRIGGER insbef_default_id
BEFORE INSERT ON data.foo_log -- the partition
FOR EACH ROW
EXECUTE PROCEDURE trg_log_default_id();
Run Code Online (Sandbox Code Playgroud)
无论如何,这都会从序列中分配一个数字,更接近地模仿GENERATED ALWAYS父母的行为 - 更严格,甚至还可以防止COPY违反您的规则。手册:
COPY FROM将调用任何触发器并检查目标表上的约束。
触发器比普通的默认值要贵一些。并且它会在每行刻录一个额外的序列号,以便通过父表进行常规插入。(触发器中应该可以区分大小写,现在没有尝试。)
| 归档时间: |
|
| 查看次数: |
4722 次 |
| 最近记录: |