向所有yii2标准化极客提问。
在yii2中设置非规范化列的最佳位置是哪里?
例如,我有客户模型、分行模型、收银机模型和交易模型。在一个完美的世界中,在一个完美规范化的数据库中,我们的事务模型只有 cashregister_id
,收银机将存储 branch_id
,分支将存储 customer_id
. 但是,由于性能问题,我们有时不得不使用非规范化的交易模型,其中包含以下内容:
收银机id
分支机构id
客户id
创建事务时,我要存储所有3个值。设置
$transaction->branch_id = $transaction->cashRegister->branch_id;
$transaction->customer_id = $transaction->cashRegister->branch->customer_id;
但是在控制器中感觉不正确。
一种解决方案是在事务模型的aftersave()中执行此操作,并使这些列为只读。但这似乎更好,但并不完美。
我想知道什么是最佳实践,或者在哪里设置这些重复列以确保数据完整性的最佳位置?
2条答案
按热度按时间bfnvny8b1#
我也有过类似的问题
afterSave()
或者beforeSave()
起初看起来是一个很好的解决方案,但最终导致难以维护意大利面条代码。我最终创建了单独的组件来管理这种关系。比如:那你不是在创建或更新
Transaction
直接建模,您总是使用此组件并封装其中的所有逻辑。activerecord的工作原理更像是一种数据表示,不包含任何高级业务逻辑。在某些情况下,它看起来比$model->load($data) && $model->save()
但毕竟,当您将所有逻辑放在一个地方并且不需要调试时,维护起来就容易多了save()
调用链(一个模型运行)save()
不同型号的afterSave()
它运行save()
不同型号的afterSave()
... 以此类推)。rjzwgtxy2#
下面是一个只支持db的解决方案。
我想你们的关系是:
一个客户有很多分支机构
分行有许多收银机
收银机有许多交易
相应的模式可以是:
(注意:这应该是你问题的一部分-因此我们不需要猜测。)
如果要包含冗余列(
branch_id
以及customer_id
)在transactions
表中,您应该使它们成为外键的一部分。但首先你需要包括一个customer_id
中的列cashregisters
表,并使其成为外键的一部分。扩展模式为:
笔记:
任何外键约束都需要子表(引用)和父表(引用)中的索引,以支持约束检查。键中给定的列顺序允许我们定义每个表只有一个索引的模式。
外键应始终引用父表中的唯一键。但是在这个例子中,引用列的组合(至少)是隐式唯一的,因为它包含主键。在几乎任何其他rdbms中,您都需要在“中间”表中定义索引(
branches
以及cashregisters
)作为UNIQUE
. 然而,这在mysql中不是必需的。复合外键将负责数据的完整性/一致性。示例:如果您有一个分支条目
branch_id = 2
以及customer_id = 1
-你不能把收银机branch_id = 2
以及customer_id = 3
,因为这将违反外键约束。您可能需要更多的索引来进行查询。很可能你需要
cashregisters(branch_id)
以及transactions(cashregister_id)
. 使用这些索引,您甚至可能不需要更改orm关系代码(尽管afaik yii支持复合外键。)您可以定义关系,如“客户有许多交易”。以前需要使用“has many through”,包括两个中间/桥接表。在许多情况下,这将节省两个连接。
如果希望数据库维护冗余数据,可以使用以下触发器:
现在您可以插入新条目,而无需定义冗余值:
请参见演示:https://www.db-fiddle.com/f/fe7kvxitczbx3gfa81njze/0
如果您的业务逻辑允许更新关系,那么应该使用
ON UPDATE CASCADE
. 这将通过关系链向下到transactions
table。