Php 在Yii2中设置非规范化列的最佳实践

Php 在Yii2中设置非规范化列的最佳实践,php,mysql,yii2,database-normalization,Php,Mysql,Yii2,Database Normalization,向所有Yii2标准化极客提问 在Yii2中设置非规范化列的最佳位置是哪里 例如,我有客户、分行、收银机和交易等型号。 在一个完美的世界中,在一个完美规范化的数据库中,交易模型将只有收银机id,收银机将存储分行id,而分行将存储客户id。但是,由于性能问题,我们有时不得不使用非规范化的事务模型,其中包含以下内容: 收银机 分支机构id 客户识别码 创建事务时,我希望存储所有3个值。背景 $transaction->branch_id = $transaction->cashRegist

向所有Yii2标准化极客提问

在Yii2中设置非规范化列的最佳位置是哪里

例如,我有客户分行收银机交易等型号。 在一个完美的世界中,在一个完美规范化的数据库中,交易模型将只有
收银机id
收银机将存储
分行id
,而分行将存储
客户id
。但是,由于性能问题,我们有时不得不使用非规范化的事务模型,其中包含以下内容:

  • 收银机
  • 分支机构id
  • 客户识别码
  • 创建事务时,我希望存储所有3个值。背景

    $transaction->branch_id = $transaction->cashRegister->branch_id;
    $transaction->customer_id = $transaction->cashRegister->branch->customer_id;
    
    但是,在控制器中感觉不正确

    一种解决方案是在事务模型中的aftersave()中执行此操作,并将这些列设置为只读。但这似乎更好,但并不完美


    我想知道什么是设置这些重复列的最佳做法,或者在哪里设置这些重复列的最佳位置,以确保数据的完整性?

    我曾经遇到过类似的问题,使用
    afterSave()
    beforeSave()
    一开始看起来是一个很好的解决方案,但最终导致难以维护意大利面代码。我最终创建了单独的组件来管理这种关系。比如:

    class TransactionsManager extends Component {
    
        public function createTransaction(TransactionInfo $info, CashRegister $register) {
            // magic
        }
    }
    

    然后,您不会直接创建或更新
    事务
    模型,而是始终使用此组件并将所有逻辑封装在其中。ActiveRecord的工作原理更像是一种数据表示,不包含任何高级业务逻辑。在某些情况下,它看起来比
    $model->load($data)&&&$model->save()
    更复杂,但毕竟,当您将所有逻辑放在一个地方,并且不需要调试
    save()
    调用链时,维护起来就容易多了(一个模型在运行
    afterSave()
    中运行不同模型的
    save()
    afterSave()
    …等中的不同型号。

    以下是一个仅适用于DB的解决方案

    我想你们的关系是:

    • 客户有许多分支机构
    • 分行有许多收款机
    • 收银机有许多交易
    相应的模式可以是:

    创建表客户(
    客户id int自动增量,
    客户数据文本,
    主键(客户id)
    );
    创建表分支(
    分支id int自动增量,
    客户id int不为空,
    分支_数据文本,
    主键(分支id),
    索引(客户id),
    外键(客户id)引用客户(客户id)
    );
    创建表现金出纳(
    收银机id自动递增,
    分支id int不为空,
    收银机数据文本,
    主键(收银机id),
    索引(分支机构id),
    外键(分支id)引用分支(分支id)
    );
    创建表事务(
    事务id int自动增量,
    收银机\u id int不为空,
    事务处理单元数据文本,
    主键(事务_id),
    索引(收银机id),
    外键(收银机id)引用收银机(收银机id)
    );
    
    (注意:这应该是你问题的一部分,所以我们不需要猜测。)

    如果要在
    交易
    表中包含冗余列(
    分支机构id
    客户id
    ),则应将它们作为外键的一部分。但首先,您需要在
    现金登记薄
    表中包含
    客户id
    列,并将其作为外键的一部分

    扩展模式将是:

    创建表客户(
    客户id int自动增量,
    客户数据文本,
    主键(客户id)
    );
    创建表分支(
    分支id int自动增量,
    客户id int不为空,
    分支_数据文本,
    主键(分支id),
    索引(客户id、分支机构id),
    外键(客户id)引用客户(客户id)
    );
    创建表现金出纳(
    收银机id自动递增,
    分支id int不为空,
    客户id int不为空,
    收银机数据文本,
    主键(收银机id),
    索引(客户id、分行id、收银机id),
    外键(客户id、分支机构id)
    参考分行(客户id、分行id)
    );
    创建表事务(
    事务id int自动增量,
    收银机\u id int不为空,
    分支id int不为空,
    客户id int不为空,
    事务处理单元数据文本,
    主键(事务_id),
    索引(客户id、分行id、收银机id),
    外键(客户id、分行id、收银机id)
    参考收银机(客户id、分行id、收银机id)
    );
    
    注:

    • 任何外键约束都需要在子(引用)表和父(引用)表中建立索引,以支持约束检查。键中给定的列顺序允许我们定义每个表只有一个索引的模式
    • 外键应始终引用父表中的唯一键。但是,在本例中,引用列的组合(至少)是隐式唯一的,因为它包含主键。在几乎任何其他RDBMS中,您都需要将“中间”表(
      分支机构
      收银机
      )中的索引定义为
      唯一
      。然而,这在MySQL中不是必需的
    • 复合外键将负责数据完整性/一致性。示例:如果您有一个分行分录,其
      分行id=2
      客户id=1
      -您将无法插入一个分行id=2
    客户id的收银机