Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在子行和父行之间没有精确的外键匹配的情况下,如何在PostgreSQL中实施一对多关系?_Postgresql_Triggers_Foreign Keys_One To Many - Fatal编程技术网

在子行和父行之间没有精确的外键匹配的情况下,如何在PostgreSQL中实施一对多关系?

在子行和父行之间没有精确的外键匹配的情况下,如何在PostgreSQL中实施一对多关系?,postgresql,triggers,foreign-keys,one-to-many,Postgresql,Triggers,Foreign Keys,One To Many,我在对主键中包含开始和结束日期的父表以及主键中包含时间戳的子表的数据进行建模时遇到问题,该子表必须在父表的开始和结束日期的范围内。事实上,这个问题是嵌套的,因为父表实际上是另一个表的子表——“祖父母”表,它的主键中也有开始和结束日期;父表的开始日期和结束日期同样必须在祖父母表的开始日期和结束日期的范围内 作为背景,我在一家水处理公司工作。作为处理合同的一部分,我们通过将水处理设备部署到各个现场来处理水。更具体地说: 有许多场地需要对其水进行处理 这些网站与我们签订合同,以便我们能够处理水。合同

我在对主键中包含开始和结束日期的父表以及主键中包含时间戳的子表的数据进行建模时遇到问题,该子表必须在父表的开始和结束日期的范围内。事实上,这个问题是嵌套的,因为父表实际上是另一个表的子表——“祖父母”表,它的主键中也有开始和结束日期;父表的开始日期和结束日期同样必须在祖父母表的开始日期和结束日期的范围内

作为背景,我在一家水处理公司工作。作为处理合同的一部分,我们通过将水处理设备部署到各个现场来处理水。更具体地说:

  • 有许多场地需要对其水进行处理
  • 这些网站与我们签订合同,以便我们能够处理水。合同总是有一个已知的开始日期,但合同可以是一段特定的时间,也可以是无限期的,因此结束日期可以是已知的,也可以是未知的(因此可以为空的结束日期)
  • 为满足合同要求,一次在现场部署一台水处理机。如果一台机器在合同中间出现故障,需要更换,我们用同一合同替换另一台机器。
  • 当机器根据合同处理水时,我们从它们那里收集处理数据
因此,我们必须跟踪
站点
s、
治疗合同
s、
机器部署
s、
机器
s和
治疗数据点
s。一个
站点
可以有多个
治疗合同
s,一个
治疗合同
可以有多个
机器部署
和多个
治疗数据点
s,一个
机器
可以有多个
机器部署
s

因此,我试图建模的数据的简化版本如下:

CREATE TABLE public.site
(
    id integer NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE public.treatment_contract
(
    site_id integer NOT NULL,
    start_date date NOT NULL,
    end_date date,
    PRIMARY KEY (site_id, start_date, end_date)
    CONSTRAINT fk_treatment_contract__site FOREIGN KEY (site_id)
        REFERENCES public.site (site_id) MATCH SIMPLE
);

CREATE TABLE public.machine_deployment
(
    site_id integer NOT NULL,
    machine_id integer NOT NULL,
    start_date date NOT NULL,
    end_date date,
    PRIMARY KEY (site_id, machine_id, start_date, end_date),
    CONSTRAINT fk_machine_deployment__machine FOREIGN KEY (machine_id)
        REFERENCES public.machine (id) MATCH SIMPLE,
    <some provision to require that machine_deployment.start_date and machine_deployment.end_date are between treatment_contract.start_date and treatment_contract.end_date, and that machine_deployment.site_id matches treatment_contract.site_id>
);

CREATE TABLE public.treatment_datapoint
(
    site_id integer NOT NULL,
    time_stamp timestamp NOT NULL,
    PRIMARY KEY (site_id, time_stamp),
    <some provision to require time_stamp is between treatment_contract.start_date and treatment_contract.end_date, and that treatment_datapoint.site_id matches treatment_contract.site_id>
);

CREATE TABLE public.machine
(
    id integer NOT NULL,
    PRIMARY KEY (id)
);
创建表public.site
(
id整数不为空,
主键(id)
);
创建表public.treatment\u contract
(
站点id整数不为空,
开始日期不为空,
结束日期,
主键(站点id、开始日期、结束日期)
约束fk\U处理\U合同\U站点外键(站点id)
引用public.site(site_id)匹配简单
);
创建表public.machine\u部署
(
站点id整数不为空,
机器id整数不为空,
开始日期不为空,
结束日期,
主键(站点id、机器id、开始日期、结束日期),
约束fk_机器部署_机器外键(机器id)
引用public.machine(id)匹配简单,
);
创建表public.treatment\u数据点
(
站点id整数不为空,
时间戳时间戳不为空,
主键(站点id、时间戳),
);
创建表public.machine
(
id整数不为空,
主键(id)
);
我不知道如何继续,因为PostgreSQL只能在所有外键字段之间完全匹配的情况下强制执行外键关系-外键约束中没有规定可以在parent.start和parent.end之间强制执行类似于
child.timestamp的内容
treatment\u数据点
应具有
treatment\u合同
的外键,因为没有
treatment\u合同的
treatment\u数据点
将毫无意义,但似乎无法强制执行此外键关系。答案只是使用触发器吗?我总是被告知不要使用触发器来定义父:子关系,因为外键就是用来定义父:子关系的


无论如何,必须有一种方法对此进行建模,因为我无法想象我是唯一一个需要强制子表中的日期在父表中定义的范围内的人。

简而言之:在没有外键的情况下强制关系-创建一个

要使您的模型正常工作,您必须有一个
治疗合同
的外键,并且由于
治疗合同
的主键包含字段
站点id
开始日期
结束日期
您必须将
合同开始日期
合同结束日期
添加到需要引用合同的表中,即
机器部署
治疗数据点

为了让您的生活更轻松,我建议不要将NULL用于合同和机器部署的未知结束日期。我认为它是一个“神奇数字”,意思是“无限”。这不是必需的,但使检查更简单

此外,我还添加了一个选项,以确保合同在开始后结束

最后,您可以使用检查约束来验证部署开始和结束以及数据点时间戳


在下面的示例中,我在支票中使用和。这是为了方便。使用比较运算符可以获得相同的结果(
FK约束是一个特殊的东西,它不是FK约束,因此这样称呼是没有帮助和误导性的。不是所有的约束都可以在SQL DBMS中声明性地表达。另一种选择是triggers.PS在考虑发布之前,请阅读手册和谷歌任何错误消息,以及您的许多清晰、简洁和准确的措辞r问题/问题/目标,有/没有您的特定名称/字符串/数字,'site:stackoverflow.com'&tags;阅读许多答案。如果您发布一个问题,请使用一个短语作为标题。反映您的研究。@philipxy我具体在哪里将某些内容错误标记为FK约束?我在帖子中多次提到外键。此外,没有错误消息s适用于此问题,因为无法定义外键以将值与范围相匹配。您是否建议我为基于范围的外键编写语法,尝试将其输入PostgreSQL,然后
CREATE TABLE public.site
(
    id integer NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE public.treatment_contract
(
    site_id integer NOT NULL,
    start_date date NOT NULL,
    end_date date NOT NULL,
    PRIMARY KEY (site_id, start_date, end_date),
    CONSTRAINT fk_treatment_contract__site FOREIGN KEY (site_id)
        REFERENCES public.site (id) MATCH SIMPLE
);

CREATE TABLE public.machine
(
    id integer NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE public.machine_deployment
(
    site_id integer NOT NULL,
    machine_id integer NOT NULL,
    contract_start_date date NOT NULL,
    contract_end_date date NOT NULL,
    start_date date NOT NULL,
    end_date date NOT NULL,
    PRIMARY KEY (site_id, machine_id, start_date, end_date),
    CONSTRAINT fk_machine_deployment__machine FOREIGN KEY (machine_id)
        REFERENCES public.machine (id) MATCH SIMPLE,
    CONSTRAINT fk_machine_deployment__treatment_contract FOREIGN KEY (site_id, contract_start_date, contract_end_date)
        REFERENCES public.treatment_contract(site_id, start_date, end_date),
    CONSTRAINT chk_machine_deploiment_period CHECK (start_date <= end_date),    
    CONSTRAINT chk_machine_deploiment_in_contract CHECK (pg_catalog.daterange(start_date, end_date,'[]') <@ pg_catalog.daterange(contract_start_date, contract_end_date, '[]'))
);

CREATE TABLE public.treatment_datapoint
(
    site_id integer NOT NULL,
    contract_start_date date NOT NULL,
    contract_end_date date NOT NULL,
    time_stamp timestamp NOT NULL,
    PRIMARY KEY (site_id, time_stamp),
    CONSTRAINT fk_treatment_datapoint__treatment_contract FOREIGN KEY (site_id, contract_start_date, contract_end_date)
        REFERENCES public.treatment_contract(site_id, start_date, end_date),
    CONSTRAINT chk_datapoint_in_contract CHECK (time_stamp::date <@ pg_catalog.daterange(contract_start_date, contract_end_date, '[]'))
);