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 次 |
最近记录: |