优化数据库结构

时间:2011-10-04 15:29:44

标签: php javascript mysql

我正在为我们的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;
    }
}

所以,我的问题是,我该如何实际接近这个?全文索引;也许学生表的总分值会在每次输入交易时得到更新;大众交易(即多个同一类别的同一点的学生)被分组到一个数据库行?这些都是我所想到的,但我喜欢有比自己更多数据库知识的人来提供启发。

示例记录 Example records

表格结构 Table structure

表格概述 Table overview

分配点数界面 Assign Points interface

非常感谢提前。

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:2)

根据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

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

答案 2 :(得分:1)

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

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

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

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