几年前,我为11-16岁的学生设计了一个奖励系统,包括PHP,JavaScript和MySQL。
前提很简单:
数据库结构也很简单(可能太多了):
CREATE TABLE `transactions` (
`Transaction_ID` int(9) NOT NULL auto_increment,
`Datetime` date NOT NULL,
`Giver_ID` int(9) NOT NULL,
`Recipient_ID` int(9) NOT NULL,
`Points` int(4) NOT NULL,
`Category_ID` int(3) NOT NULL,
`Reason` text NOT NULL,
PRIMARY KEY (`Transaction_ID`),
KEY `Giver_ID` (`Giver_ID`),
KEY `Datetime` (`Datetime`),
KEY `DatetimeAndGiverID` (`Datetime`,`Giver_ID`),
KEY `Recipient_ID` (`Recipient_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=249069 DEFAULT CHARSET=latin1
CREATE TABLE `categories` (
`Category_ID` int(9) NOT NULL,
`Title` varchar(255) NOT NULL,
`Description` text NOT NULL,
`Default_Points` int(3) NOT NULL,
`Groups` varchar(125) NOT NULL,
`Display_Start` datetime default NULL,
`Display_End` datetime default NULL,
PRIMARY KEY (`Category_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `rewards` (
`Reward_ID` int(9) NOT NULL auto_increment,
`Title` varchar(255) NOT NULL,
`Description` text NOT NULL,
`Image_URL` varchar(255) NOT NULL,
`Date_Inactive` datetime NOT NULL,
`Stock_Count` int(3) NOT NULL,
`Cost_to_User` float NOT NULL,
`Cost_to_System` float NOT NULL,
PRIMARY KEY (`Reward_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=latin1
CREATE TABLE `purchases` (
`Purchase_ID` int(9) NOT NULL auto_increment,
`Datetime` datetime NOT NULL,
`Reward_ID` int(9) NOT NULL,
`Quantity` int(4) NOT NULL,
`Student_ID` int(9) NOT NULL,
`Student_Name` varchar(255) NOT NULL,
`Date_DealtWith` datetime default NULL,
`Date_Collected` datetime default NULL,
PRIMARY KEY (`Purchase_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=6133 DEFAULT CHARSET=latin1
系统运行良好一段时间。它现在开始大量减慢某些查询。
基本上,每次我需要访问学生的奖励积分总数时,所需的查询需要年龄。以下是一些示例查询及其运行时间:
SELECT CONCAT( s.Firstname, " ", s.Surname ) AS `Student` , s.Year_Group AS `Year Group`, SUM( t.Points ) AS `Points`
FROM frog_rewards.transactions t
LEFT JOIN frog_shared.student s ON t.Recipient_ID = s.id
WHERE t.Datetime > '2013-09-01' AND t.Category_ID NOT IN ( 12, 13, 14, 26 )
GROUP BY t.Recipient_ID
ORDER BY `Points` DESC
LIMIT 0 , 15
SELECT Recipient_ID, SUM(points) AS Total_Points FROM
{交易{1}}
现在我明白了,特别是对于第二个查询,我不应该运行一个会返回如此大量行的调用,但是系统运行的框架的局限性意味着我别无选择如果我想向教师/导师/年度经理/领导者展示学生的总奖励积分,以便查看和分析。
幸运的是,我们被迫使用的框架正在发生变化。我们现在将使用oAuth而不是可怕的,过时的JavaScript小部件格式。
不幸的是 - 或者,我想,幸运的是 - 这意味着我们必须重写相当多的系统。
重写系统时我打算查看的主要方面之一是数据库结构。随着时间的推移,它只会变大,所以我需要做一些面向未来的工作。
因此,我的主要问题是:存储学生总分的最有效和最有效的方法是什么?
我能想到的唯一想法就是有一个名为GROUP BY Recipient_ID
的单独表格,其中包含totals
和Student_ID
字段。每当一名工作人员给出一些要点时,它会在Points
表中添加一行,但也会更新transactions
表。
效率这么高吗?拥有totals
类型字段会有效吗?我将如何更新/保持最佳状态?
除了主要问题之外,如果有人对数据库表的优化有一般改进的建议,请告诉我。
提前致谢, 邓肯
答案 0 :(得分:3)
您的设计没有什么特别的错误,应该让它像您报告的那样慢。我认为必须有其他因素起作用,例如服务器在运行时过载或慢速运行。只有你能够发现是否是这种情况。
为了测试您的设计,我在台式计算机上运行的2008 SQL Server上重新创建了它。我有一个标准的计算机,单个硬盘,而不是SSD,没有raid等。所以在适当的数据库服务器上,结果应该更好。当你使用MySQL时,我不得不对设计进行一些更改,但是没有任何更改会影响性能,只是这样我才能在我的数据库上运行它。
这是我使用的表结构,我不得不猜测你在Student
和Staff
表中会有什么,因为你没有说明这些。我也冒昧地更改了Transaction
和Giver_ID
Receiver_ID
表格中的字段名称,因为我假设只有员工给予积分,学生才能收到积分。
我生成了随机数据来填充表格,其数量与您在数据库中所说的行数相同
我跑了两个你说已经花了很长时间的查询,我已经改变它们以适合我的设计,但我(希望)结果是相同的
SELECT TOP 15
Firstname + ' ' + Surname
,Year_Group
,SUM(Points) AS Points
FROM points.[Transaction]
INNER JOIN points.Student ON points.[Transaction].Student_ID = points.Student.Student_ID
WHERE [Datetime] > '2013-09-01'
AND Category_ID NOT IN ( 12, 13, 14, 26 )
GROUP BY Firstname + ' ' + Surname
,Year_Group
ORDER BY SUM(Points) DESC
SELECT Student_ID
,SUM(Points) AS Total_Points
FROM points.[Transaction]
GROUP BY Student_ID
返回的两个查询结果大约为1秒。除了主键上默认生成的CLUSTERED
索引之外,我没有在表上创建任何其他索引。查看执行计划,查询处理器估计实现以下索引可以提高查询成本 81.0309%
CREATE NONCLUSTERED INDEX [<Name of Missing Index>]
ON [points].[Transaction] ([Datetime],[Category_ID])
INCLUDE ([Student_ID],[Points])
正如其他人所评论的那样,在花费大量时间重新设计数据库之前,我会先寻找瓶颈。
<强>更新强>
我意识到我从未真正解决过您的具体问题:
什么是最有效和最有效的学生存储方式 总分?
我能想到的唯一想法是有一个单独的表叫做 带有Student_ID和Points字段的总计。每次都是员工 给出了一些观点,它在事务表中添加了一行但是 还会更新总计表。
除非您已经探索了其他可能的方法来加速数据库,否则我不建议保留单独的总分。一个单独的计数器可能与事务不同步,然后你必须调和所有内容并追踪出错的地方,以及正确的总数应该是什么。
在尝试提高速度之前,您应始终专注于保持数据的正确性和一致性。大多数情况下,正确(标准化)的数据模型运行得足够快。
在我工作的一个地方,我们发现加速数据库的最具成本效益的方法就是升级硬件;比花费许多工时重新设计数据库要快得多,便宜得多:)