如何在 PostgreSQL 中使用外键约束正确引用超表?

问题描述

#错误描述: 如果在创建表时定义了外键,则可以创建一个具有到超表中的外键的表

#为了重现,有下一个表格:

CREATE TABLE ids ( 
    measurement_id int DEFAULT 0,description text DEFAULT 0,m_id bigserial NOT NULL,service_id int  DEFAULT NULL,time bigint NOT NULL DEFAULT cast((EXTRACT(EPOCH FROM Now() AT TIME ZONE 'UTC') * 1000) as bigint),user_id int  DEFAULT NULL,end_time DOUBLE PRECISION DEFAULT 0,start_time int NOT NULL DEFAULT 0
);

CREATE INDEX ON ids (time DESC,user_id);
CREATE INDEX ON ids (time DESC,service_id);

SELECT create_hypertable('ids','start_time',chunk_time_interval => 604800016);

---------

CREATE TABLE IF NOT EXISTS metrics (
  id bigserial NOT NULL,duration real DEFAULT NULL,metric integer DEFAULT 0,m_id bigint NOT NULL,time bigint NOT NULL DEFAULT 0   
);

ALTER TABLE metrics ADD PRIMARY KEY (time,m_id);

CREATE INDEX ON metrics (time DESC);
CREATE INDEX ON metrics (time DESC,measurement );
CREATE INDEX ON metrics (time DESC,m_id );

grant all privileges on ids,metrics to your_db_user;

SELECT create_hypertable('metrics','time',chunk_time_interval => 604800016);

SELECT table_catalog,table_schema,table_name,privilege_type FROM  information_schema.table_privileges WHERE  grantee = 'your_db_user';

---------

DROP TABLE IF EXISTS resource;
CREATE TABLE resource(
id          int NOT NULL,cpu         text DEFAULT 0,storing     text DEFAULT 0,memory      text DEFAULT 0
); 

ALTER TABLE resource ADD PRIMARY KEY (id);

CREATE SEQUENCE resource_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE resource_id_seq
  OWNER TO your_db_user;

ALTER TABLE resource ALTER COLUMN id SET DEFAULT nextval('resource_id_seq'::regclass);

---------

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

DROP TABLE IF EXISTS ns;

CREATE TABLE ns(
id                    bigint NOT NULL,uuid                  uuid NOT NULL DEFAULT uuid_generate_v4 (),availability          double precision,faultTolerance        boolean,activated             boolean,UNIQUE (id,uuid),PRIMARY KEY(id),CONSTRAINT fk_resource
     FOREIGN KEY(id)
         REFERENCES resource(id)
         ON DELETE CASCADE
);

CREATE SEQUENCE ns_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 1;
ALTER TABLE ns_id_seq
  OWNER TO your_db_user;

ALTER TABLE ns ALTER COLUMN id SET DEFAULT nextval('ns_id_seq'::regclass);

---------

DROP TABLE IF EXISTS authentication;

CREATE TABLE authentication(
id                    integer NOT NULL,username              character varying(255) NOT NULL,password              character varying(255) NOT NULL,host                  character varying(255) NOT NULL,port                  character varying(10) NOT NULL,PRIMARY KEY(id)
);

CREATE SEQUENCE auth_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE auth_id_seq
  OWNER TO your_db_user;

ALTER TABLE authentication ALTER COLUMN id SET DEFAULT nextval('auth_id_seq'::regclass);

---------

DROP TABLE IF EXISTS job;

CREATE TABLE job(
id                    int NOT NULL,interval              integer NOT NULL,auth_id               integer REFERENCES authentication (id),ns_id                 integer REFERENCES ns (id),UNIQUE (auth_id,ns_id),PRIMARY KEY(id)
);

ALTER TABLE job
ADD CONSTRAINT fk_auth_id
FOREIGN KEY (id) REFERENCES authentication (id)
ON DELETE CASCADE
DEFERRABLE INITIALLY DEFERRED;

ALTER TABLE job
ADD CONSTRAINT fk_ns_id
FOREIGN KEY (id) REFERENCES ns (id)
ON DELETE CASCADE
DEFERRABLE INITIALLY DEFERRED;

CREATE SEQUENCE job_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE job_id_seq
  OWNER TO your_db_user;

ALTER TABLE job ALTER COLUMN id SET DEFAULT nextval('job_id_seq'::regclass);

---------

DROP TABLE IF EXISTS job_metric;

CREATE TABLE job_metric (
  id                       int NOT NULL,j_id                     int NOT NULL REFERENCES job (id),mj_id                   bigint  NOT NULL,jm_time                 bigint NOT NULL 
);
CREATE INDEX ON job_metric (jm_time DESC);
CREATE INDEX ON job_metric (jm_time DESC,id);
CREATE INDEX ON job_metric (jm_time DESC,mj_id);

ALTER TABLE job_metric ADD PRIMARY KEY (jm_time,id);

grant all privileges on job_metric to your_db_user;

SELECT create_hypertable('job_metric','jm_time',chunk_time_interval => 604800016);

CREATE SEQUENCE mjob_metric_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE mjob_metric_id_seq
  OWNER TO your_db_user;

ALTER TABLE job_metric ALTER COLUMN id SET DEFAULT nextval('mjob_metric_id_seq'::regclass);
---------

创建表后,我在 Postgresql 12.6 的数据库中使用了 solution proposed by @Laurenz,使用 timescaledb 1.7.5 的扩展名,如下所示:

#用适当的值填充表格:

UPDATE job_metric AS jm_point
SET jm_time = qm.time
FROM metrics AS qm
WHERE qm.m_id = jm_point.mj_id;

#然后将其设置为 NOT NULL:

ALTER TABLE job_metric ALTER jm_time SET NOT NULL;

#定义外键:

ALTER TABLE job_metric
   ADD FOREIGN KEY (mj_id,jm_time)
   REFERENCES metrics (time,m_id) MATCH FULL;

#最后一个引用表启用外键的响应:查询在40毫秒内成功返回。

预期行为: 这个想法是在多对多关系中使用表 job_metric 来访问作业和指标表的信息。

实际行为和错误 创建了表并创建了 FK但在 job_metric 处插入数据时无法使用,详情如下:

INSERT INTO job_metric (j_id,mj_id,jm_time) 
VALUES(13,185063,1621957192266);

错误不支持超表的外键上下文:sql 语句“ ALTER TABLE _timescaledb_internal._hyper_5_5_chunk ADD 约束“5_13_job_metric_j_id_mj_id_jm_time_fkey”外键 (j_id,jm_time) 参考 qmetrics("time",m_id) MATCH FULL " PL/pgsql 函数 _timescaledb_internal.chunk_constraint_add_table_constraint(_timescaledb_catalog.chunk_constraint) EXECUTE sql 状态的第 42 行:0A000

***根据https://docs.timescale.com/timescaledb/latest/overview/limitations/##distributed-hypertable-limitations,看起来上面的错误是hypertable限制的一部分:

不支持引用超表的外键约束。

#Request: 鉴于上述信息和错误,有没有人知道在 DB 级别使用 timescaledb 扩展和主要是 hypertables 建立关系(多对多或一对多)的任何解决方案?


实际上,当我尝试使用 Django Rest Framework 在表 metrics 和 job_metric 之间创建多对多关系时,我得到了类似的上述错误

class Job_Metrics(models.Model):
job = models.OnetoOneField(Job,on_delete=models.CASCADE)
metrics = models.ManyToManyField(Metrics)
time = models.IntegerField(default=0)

运行应用程序指标直接指出metrics_db: $ python3 manage.py migrate metrics --database=metrics_db

要执行的操作:应用所有迁移:指标运行迁移:应用 metrics.0002_job...Traceback(最近一次调用最后一次):文件 "/var/myproject/myprojectenv/lib/python3.8/site-packages/django/db/backends/utils.py",第 84 行,在 _execute 中返回 self.cursor.execute(sql,params) psycopg2.errors.FeatureNotSupported:超表的外键是 不支持

如果有人知道解决方案或有想法在 REST API 级别处理上述错误,请分享您的想法,目的是访问数据关联表(指标和作业)并在需要时一起修改它们删除例如job_metric。到目前为止,使用timescaledb扩展的hypertables修改似乎不是一个可行的解决方案。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)