【问题标题】:Updating the intermediary table Best practice PHP, MySQL更新中间表 最佳实践 PHP、MySQL
【发布时间】:2018-07-06 10:50:36
【问题描述】:

假设我有两张表学生和科目之间的多对多关系。所以我们创建一个中间表来映射数据。可以说它是students_subjects。 student_subjects 表有 2 列,即 student_id 和 subject_id。

+------------+--------------+
| student_id | student_name |
+------------+--------------+
|            |              |
+------------+--------------+

+------------+--------------+
| subject_id | subject_name |
+------------+--------------+
|            |              |
+------------+--------------+

+------------+------------+
| student_id | subject_id |
+------------+------------+
|            |            |
+------------+------------+

直到最近,students_subjects(学生选择科目)有更新,我所做的只是删除了学生插入新科目的所有科目。

+------------+------------+----------+
| student_id | subject_id | selected |
+------------+------------+----------+
| 1          | 2          | 1        |
+------------+------------+----------+
| 1          | 2          | 0        |
+------------+------------+----------+

但最近我在这个中间表中添加了一个新列作为选择。因此,如果学生选择了新科目并取消选择了一些旧科目,我会为新科目添加 active 并为取消选择的科目添加 non_active。 (我在 PHP 中通过获取新选择的科目和以前的科目来做到这一点。然后比较两者。当学生选择科目时,我检查学生拥有的所有科目[活跃和非活跃]。如果新选择的科目以前没有活跃我激活它们并添加新的主题。)

这样做的最佳做法是什么。我有更好的方法来实现这一点吗?

【问题讨论】:

  • 这不是答案。一种推荐。您可以使用 ORM 更轻松地找出这种类型的关系。
  • 我不是 DBA,但可能有两个“生效日期”列?一个带有“有效开始”,一个带有“有效结束”(在创建行时可能会填充也可能不会填充)。那么,如果课程在开始之后和结束之前,那么它是“活跃的”?
  • 使用新的“selected”列,您是否在表中查询为“1”的学生id和fine id并将这些科目添加到学生和科目表中?然后将student_subjects表中的“1”标志设置为“0”?
  • 当学生选择科目时,我会检查学生拥有的所有科目(活跃和非活跃)。如果新选择的主题之前没有活跃的主题,我会激活它们并添加新的主题。
  • 您是否要跟踪学生过去是否注册过某个科目?

标签: php mysql database time-complexity


【解决方案1】:

我确实认为您的第一种方法(删除所有关联的行并插入新条目)要简单得多。

如果仍然想要selected 字段,一个简单的方法是创建唯一的复合索引并使用INSERT ... ON DUPLICATE KEY UPDATE。这也可以防止意外插入相同的user_idsubject_id 对。首先创建索引:

ALTER TABLE `students_subjects` ADD UNIQUE `unique_student_subject`(`student_id`, `subject_id`);

然后,只需将学生的selected 字段更新为0 并插入学生选择的科目。使用PDO 的示例:

$studentId = 1;
$newlySelectedSubjects[] = ['subject_id' => 3];
$newlySelectedSubjects[] = ['subject_id' => 4];

$db = new PDO("connection string here", $username, $password);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$db->beginTransaction();

try{
    $qry = 'UPDATE students_subjects SET selected = 0 WHERE student_id = :studentId';

    $stmt = $db->prepare($qry);
    $stmt->execute(['studentId' => $studentId]);

    $insertQry = 'INSERT INTO students_subjects (student_id, subject_id, selected)' 
                 .'VALUES (:studentId, :subjectId, 1) '
                 .'ON DUPLICATE KEY UPDATE '
                 .'selected = 1';
    $insertStmt = $db->prepare($insertQry);

    foreach ($newlySelectedSubjects as $subject) {

        $params = ['studentId' => $studentId, 'subjectId' => $subject['subject_id']];
        $insertStmt->execute($params);
    }
    $db->commit();
} catch (PDOException $e) {
    //do something with exception
    $db->rollBack();
}

如果表中已经存在student_id = 1subject_id = 3 的学生,selected 字段将设置为 1,如果不是新的记录将被插入。

如果您需要跟踪用户科目选择和取消选择,我建议实施某种审计跟踪,并留下students_subjects 只保留学生和科目之间的关系。借助 mysql TRIGGER 可以更轻松。审计跟踪表的简化示例:

+------------+------------+----------+---------------------+
| student_id | subject_id | action   | action_datetime     |
+------------+------------+----------+---------------------+
| 1          | 2          | remove   | 2018-01-01 00:01:23 |
+------------+------------+----------+---------------------+
| 1          | 4          | add      | 2018-01-01 01:07:45 |
+------------+------------+----------+---------------------+

【讨论】:

  • 我认为这种方法很棒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-28
  • 1970-01-01
相关资源
最近更新 更多