如何解决如何在子行和父行之间没有精确外键匹配的 PostgreSQL 中强制执行一对多关系?
我在建模数据时遇到问题,父表的主键有开始和结束日期,子表的主键有时间戳,必须在父表的开始和结束范围内日期。事实上,这个问题是嵌套的,因为该父表实际上是另一个表的子表——一个“祖父”表——它的主键也有开始和结束日期;父表的开始和结束日期也必须在祖父表的开始和结束日期范围内。
作为背景,我在一家水处理公司工作。作为处理合同的一部分,我们通过将水处理机部署到各个地点来处理水。更具体地说:
- 有很多地方需要对水进行处理。
- 这些站点与我们签订合同,以便我们处理水。合同总是有一个已知的开始日期,但合同可以是特定的时间段或无限期,因此结束日期可以是已知的或未知的(因此可以为 NULL 的结束日期)
- 一次部署一台水处理机,以满足合同要求。如果一台机器在签订合同的过程中发生故障需要更换,我们会用同一合同下的另一台机器更换它。
- 当机器根据合同处理水时,我们会从它们那里收集处理数据。
因此,我们必须跟踪 site
s、treatment_contract
s、machine_deployment
s、machine
s 和 treatment_datapoint
s。一个site
可以有多个treatment_contract
,一个treatment_contract
可以有多个machine_deployments
和多个treatment_datapoint
,一个machine
可以有多个{ {1}} 秒。
所以我尝试建模的数据的简化版本是这样的:
machine_deployment
我不确定如何进行,因为 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,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,machine_id,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,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)
);
的内容。 child.timestamp BETWEEN parent.start AND parent.end
应该有一个指向 treatment_datapoint
的外键,因为没有 treatment_contract
的 treatment_datapoint
没有意义,但似乎没有办法强制执行这种外键关系。答案只是使用触发器吗?我一直被告知要避免使用触发器来定义父子关系,因为这就是外键的用途。
无论如何,必须有一种方法来对此进行建模,因为我无法想象我是唯一一个需要强制子表中的日期在定义的范围内的人父表。
解决方法
简而言之:在没有外键的情况下强制建立关系 - 制作一个。
要使您的模型正常工作,您必须有一个指向 treatment_contract
的外键,并且由于 treatment_contract
的主键包含字段 site_id
、start_date
、{{1} } 您必须将 end_date
和 contract_start_date
添加到您需要引用合同的表中,即 contract_end_date
和 machine_deployment
。
为了让您的生活更轻松,我建议您不要将 NULL 用于合同和机器部署的未知结束日期。我认为它是一个“幻数”,意思是“无穷大”。这不是必需的,但可以简化检查。
另外,我会添加一个 check constraint 以确保合同在开始后结束。
最后,您可以使用检查约束来验证部署开始和结束以及数据点时间戳。
在下面的示例中,我在支票中使用了 daterange 和 range operators。这是为了方便。您可以使用比较运算符 (treatment_datapoint
,<
...) 获得相同的结果。
我建议的架构变体是:
<=
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。