数据库
首页 > 数据库> > 设计问题:可过滤的属性,SQL

设计问题:可过滤的属性,SQL

作者:互联网

我的数据库中有两个表,即操作和设备.操作需要零个或多个属性.但是,属性的​​归属有一些逻辑:

>操作Foo需要设备A和B.
>操作杆不需要任何设备
>操作Baz需要设备B和C或D.
> Quux操作需要设备(A或B)和(C或D)

在SQL中表示这个的最佳方法是什么?

我相信人们之前已经这样做了,但我不知道从哪里开始.

(FWIW,我的应用程序是用Python和Django构建的.)

更新1:将有大约一千个操作行和大约三十个设备行.信息以CSV格式提供,类似于上面的描述:Quux,(A& B)| (C& D)

更新2:连词的级别&断开不应该太深. Quux示例可能是最复杂的,但似乎有一个A | (D& E& F)案件.

解决方法:

考虑一下如何在OO设计中对操作进行建模:操作将是常见超类操作的子类.每个子类都具有该操作所需的相应设备的强制对象成员.

使用SQL对此进行建模的方法是Class Table Inheritance.创建一个公共超级表:

CREATE TABLE Operation (
  operation_id   SERIAL PRIMARY KEY,
  operation_type CHAR(1) NOT NULL,
  UNIQUE KEY (operation_id, operation_type),
  FOREIGN KEY (operation_type) REFERENCES OperationTypes(operation_type)
);

然后,对于每种操作类型,定义一个子表,其中包含每个所需设备类型的列.例如,OperationFoo为equipA和equipB中的每一个都有一列.由于它们都是必需的,因此列不是NULL.通过为设备创建Class Table Inheritance超级表,将它们约束为正确的类型.

CREATE TABLE OperationFoo (
  operation_id   INT PRIMARY KEY,
  operation_type CHAR(1) NOT NULL CHECK (operation_type = 'F'),
  equipA         INT NOT NULL,
  equipB         INT NOT NULL,
  FOREIGN KEY (operation_id, operation_type) 
      REFERENCES Operations(operation_d, operation_type),
  FOREIGN KEY (equipA) REFERENCES EquipmentA(equip_id),
  FOREIGN KEY (equipB) REFERENCES EquipmentB(equip_id)
);

Table OperationBar不需要任何设备,因此它没有装备列:

CREATE TABLE OperationBar (
  operation_id   INT PRIMARY KEY,
  operation_type CHAR(1) NOT NULL CHECK (operation_type = 'B'),
  FOREIGN KEY (operation_id, operation_type) 
      REFERENCES Operations(operation_d, operation_type)
);

表OperationBaz有一个必需的设备equipA,然后equipB和equipC中的至少一个必须是NOT NULL.对此使用CHECK约束:

CREATE TABLE OperationBaz (
  operation_id   INT PRIMARY KEY,
  operation_type CHAR(1) NOT NULL CHECK (operation_type = 'Z'),
  equipA         INT NOT NULL,
  equipB         INT,
  equipC         INT,
  FOREIGN KEY (operation_id, operation_type) 
      REFERENCES Operations(operation_d, operation_type)
  FOREIGN KEY (equipA) REFERENCES EquipmentA(equip_id),
  FOREIGN KEY (equipB) REFERENCES EquipmentB(equip_id),
  FOREIGN KEY (equipC) REFERENCES EquipmentC(equip_id),
  CHECK (COALESCE(equipB, equipC) IS NOT NULL)
);

同样在表OperationQuux中,您可以使用CHECK约束来确保每对中至少有一个设备资源是非空的:

CREATE TABLE OperationQuux (
  operation_id   INT PRIMARY KEY,
  operation_type CHAR(1) NOT NULL CHECK (operation_type = 'Q'),
  equipA         INT,
  equipB         INT,
  equipC         INT,
  equipD         INT,
  FOREIGN KEY (operation_id, operation_type) 
      REFERENCES Operations(operation_d, operation_type),
  FOREIGN KEY (equipA) REFERENCES EquipmentA(equip_id),
  FOREIGN KEY (equipB) REFERENCES EquipmentB(equip_id),
  FOREIGN KEY (equipC) REFERENCES EquipmentC(equip_id),
  FOREIGN KEY (equipD) REFERENCES EquipmentD(equip_id),
  CHECK (COALESCE(equipA, equipB) IS NOT NULL AND COALESCE(equipC, equipD) IS NOT NULL)
);

这可能看起来很多工作.但是你问过如何在SQL中做到这一点.在SQL中执行此操作的最佳方法是使用声明性约束来为业务规则建模.显然,这要求您在每次创建新操作类型时都创建一个新的子表.当操作和业务规则从未(或几乎从未)改变时,这是最好的.但这可能不符合您的项目要求.大多数人说,“但我需要一个不需要架构改变的解决方案.”

大多数开发人员可能不会执行类表继承.更常见的是,它们只是像其他人提到的那样使用一对多表结构,并且仅在应用程序代码中实现业务规则.也就是说,您的应用程序包含的代码只能插入适用于每种操作类型的设备.

依赖于应用程序逻辑的问题在于它可能包含错误并且可能插入不满足业务规则的数据.类表继承的优点在于,通过精心设计的约束,RDBMS可以一致地强制执行数据完整性.您可以确保数据库确实无法存储不正确的数据.

但这也可能是限制性的,例如,如果您的业务规则发生变化并且您需要调整数据.这种情况下的常见解决方案是编写脚本以转储所有数据,更改模式,然后以现在允许的形式重新加载数据(Extract, Transform, and Load = ETL).

所以你必须决定:你想在应用层或数据库架构层中编码吗?使用这两种策略都有合理的理由,但无论如何都会变得复杂.

重新评论:您似乎在谈论将表达式存储为数据字段中的字符串.我建议不要这样做.数据库用于存储数据,而不是代码.您可以在约束或触发器中执行一些有限的逻辑,但代码属于您的应用程序.

如果您有太多操作要在单独的表中建模,请在应用程序代码中对其进行建模.在数据列中存储表达式并期望SQL使用它们来评估查询就像围绕大量使用eval()设计应用程序一样.

标签:mysql,sql,orm,entity-relationship
来源: https://codeday.me/bug/20190715/1463816.html