【问题标题】:Optimising Database Structure优化数据库结构
【发布时间】:2011-11-30 19:25:01
【问题描述】:

我正在为我们的 VLE 开发一个奖励系统,它使用三种独立的技术 - JavaScript 用于大多数客户端/显示处理,PHP 用于与数据库通信,MySQL 用于数据库本身。

我附上了我的“交易”表的三个屏幕截图。它的结构、一些示例记录及其详细信息的概述。

前提是工作人员会为表现良好的学生奖励积分。这可能意味着 30 名学生的班级一次获得积分。员工的积分上限为每周 300 点,目前约有 85 名员工正在访问该系统(这可能会增加)。

我现在的做法是,每笔“交易”都有一个“Giver_ID”(授予积分的员工)、一个“Recipient_ID”(获得积分的学生)、一个类别和一个原因。这样一来,每次有员工发出 30 分,我就将 30 行放入数据库中。

这在早期似乎可行,但在三周内我的数据库中已经有超过 12,000 笔交易。

此时它变得有点复杂。在“分配分数”页面(附上另一个屏幕截图)上,当老师点击他们的一个班级或搜索单个学生时,我希望显示学生的分数。我目前可以在我的系统上执行此操作的唯一方法是执行“SELECT * FROM 'transactions'”并使用以下 JS 将所​​有信息放入一个数组中:

var Points = { "Recipient_ID" : "0", "Points" : "0" };

function getPoints (data) {
    for (var i = 0; i < data.length; i++) {
        if (Points[data[i].Recipient_ID]) {
            Points[data[i].Recipient_ID] = parseInt(Points[data[i].Recipient_ID]) + parseInt(data[i].Points);
        } else {
            Points[data[i].Recipient_ID] = data[i].Points;
        }
    }
}

在内部登录系统时,这似乎运行得足够快。但是,在外部登录时,此过程大约需要 20 秒,因此在您单击/搜索几次之前不会显示学生的积分值。

我在我的 PHP 中使用以下代码来访问这些事务:

function getTotalPoints() {
    $sql = "SELECT * 
        FROM `transactions`";

    $res = mysql_query($sql);
    $rows = array(); 
    while($r = mysql_fetch_assoc($res)) {
        $rows[] = $r;
    }

    if ($rows) {
        return $rows;
    } else {
        $err = Array("err_id" => 1);
        return $err;
    }
}

所以,我的问题是,我实际上应该如何处理这个问题?全文索引;可能是一个学生表,其总分值在每次输入交易时都会更新;大量交易(即多个学生在同一类别中获得相同分数)分组到单个数据库行中?这些都是我考虑过的事情,但我希望有人比我拥有更多的 DB 知识来提供启发。

示例记录

表结构

表格概览

分配积分界面

非常感谢。

【问题讨论】:

    标签: php javascript mysql


    【解决方案1】:

    根据 Tom 的建议,您可能需要考虑进一步规范化您的数据库。我假设你现在有 3 个表:

    students (id, name, ...)

    staff (id, name, ...)

    transactions (id, student_id, staff_id, points, date, reason)

    更规范化的表格使用更少数据的更多表:

    students (id, name, ...)

    staff (id, name, ...)

    transactions (id, staff_id, points, date, reason)

    transactions_students (transaction_id, student_id)

    然后添加一个事务就变成了一个两步过程:首先创建一个事务记录,然后将多条记录插入到 transactions_students 中,每条记录都将事务链接到一个学生。请注意,您可以创建一个行为与用于选择的原始非规范化表完全相同的视图,例如:

    CREATE VIEW vw_transactions AS SELECT transactions.*, transactions_students.student_id FROM transactions INNER JOIN transactions_students WHERE transactions_students.transaction_id = transactions.id
    

    这将大大减少事务表中的记录数,并避免重复存储日期和原因。缺点是将交易链接到学生需要一个额外的连接 - 但如果您正确设置了外键和索引,这根本不是问题。

    【讨论】:

    • 谢谢 tdammers。您能否给我一个示例,说明您如何在这些表中存储事务?我实际上没有学生或教职员工表,因为所有 ID 都来自我们的 VLE,使用 Frog.API.get('users.getInfo') 调用。
    【解决方案2】:

    我会为 Recipient_ID 编制索引,这样您就可以在任何给定点专门搜索 1 个人,或者至少能够更有效地对您的数据进行分组。如果您确实选择按 category_id 分组,那么我也会向 category_id 添加一个单独或组合的索引。

    第二个建议是动态分组和聚合您的数据。例如:

    SELECT Recipient_ID, Category_ID, SUM(points) FROM transactions GROUP BY Recipient_ID, Category_ID
    

    这两个建议应该会显着提升你的表现,因为你将直接在数据库上计算,而不是在 PHP/JS 方面为学生计算总分。

    【讨论】:

      【解决方案3】:

      您的问题是您的查询:

      SELECT * FROM `transactions`
      

      随着您的数据集变得越来越大,这将需要更长的时间来加载并需要更多的内存来存储它。而是确定您具体需要哪些数据。如果是针对特定用户:

      SELECT SUM(points) FROM `transactions` WHERE Recipient_ID=[x]
      

      或者,如果您想要所有学生的所有金额:

      SELECT Recipient_ID, SUM(points) AS Total_Points FROM `transactions` GROUP BY Recipient_ID;
      

      要加快对特定字段的选择,您可以为该字段添加索引。这将加快选择速度,尤其是随着表格的增长。

      ALTER TABLE `transactions` ADD INDEX Recipient_ID (Recipient_ID);
      

      或者如果你想显示transactions中所有条目的分页列表:

      SELECT * FROM `transactions` LIMIT [page*num_records_per_page],[num_records_per_page];
      
      e.g.: SELECT * FROM `transactions` LIMIT 0,25 ORDER BY Datetime; # First 25 records
      

      【讨论】:

      • 非常感谢汤姆。这提高了我系统上许多区域的速度。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-14
      相关资源
      最近更新 更多