信息
目前正在构建一个通知页面,其中列出了所有已登录用户的通知,其中包含有关每个通知的信息。
比如没有信息
You have an unread message
有信息
<Sarah> Sent you an message
问题
因为通知需要用户名(用于消息通知)或文章标题(假设您关注一位作者并且他们发布了一篇新博客文章)等数据,所以一个通知需要从用户表中提取用户名,然后还需要提取标题博客表中的博客)这会导致我的页面甚至在本地主机上也会滞后,我猜一旦上传并在野外进行测试,情况会变得更糟。
当前代码
function showNotifications($userid){
$STH = $this->database->prepare('SELECT * FROM notifications WHERE user_id = :userid ORDER BY timestamp DESC');
$STH->execute(array(':userid' => $userid));
while($row = $STH->fetch(PDO::FETCH_ASSOC)){
$this->sortNotif($row);
}
关于下面函数的快速解释,因为我有不同类型的通知,我为特定类型创建了一堆 ID,例如类型 1 = 新消息,类型 2 = 新博客文章
function sortNotif($notif){
switch ($notif['type']) {
case "1":
$msg = $this->getMessageData($notif['feature_id']);
$user = $this->userData($msg['sender']);
echo '<li><i>'.timeAgo($notif['timestamp']).'</i><a href="user.php?username='.$user['username'].'">'.$user['first_name'].'</a> sent you a <a href="inbox.php?message='.$msg['id'].'">message</a></li>';
break;
}
}
如您所见,仅显示用户有一条新消息,它创建了 2 个查询,一旦循环了 40 条左右的通知,超过 100 位左右的用户就会对服务器造成压力。
最后的话
如果有人需要更多信息,请询问,我一定会尽快更新这个问题,谢谢!
编辑
下面是下面评论中要求的表结构。
通知
id | user_id | feature_id | type | timestamp | read
用户
id | username | password | first_name | last_name | email | verify_hash | avatar | type
消息
id | receiver | sender | replying_to | deleted | body | timestamp | read
最佳答案
改变计划,因为我误解了设置。
您将希望一次从每个“类型”表中提取所有数据,而不是基于每个通知。这意味着您需要循环遍历通知两次,一次获取所有 ID 和适当的类型,然后第二次输出结果。
function showNotifications($userid){
$STH = $this->database->prepare('SELECT * FROM notifications WHERE user_id = :userid ORDER BY timestamp DESC');
$STH->execute(array(':userid' => $userid));
// Centralized Book keeping for the types.
// When you add a new type to the entire system, add it here as well.
$types = array();
// Add the first notification type
$types["1"] = array();
// "query" is pulling all the data you need concerning a notification
$types["1"]["query"] = "SELECT m.id, u.username, u.firstname FROM messages m, users u WHERE m.sender = u.id AND m.id IN ";
// "ids" will hold the relevant ids that you need to look up.
$types["1"]["ids"] = array();
// A second type, just for show.
// $types["2"] = array();
// $types["2"]["query"] = "SELECT a.id, u.username, u.firstname FROM articles a, users u WHERE a.sender = u.id AND a.id IN ";
// $types["2"]["ids"] = array();
// Use fetchAll to gather all of the notifications into an array
$notifications = $STH->fetchAll();
// Walk through the notifications array, placing the notification id into the corret
// "ids" array in the $types array.
for($i=0; $i< count($notifications); $i++){
$types[$notifications[$i]['type']]["ids"][] = $notifications[$i]['feature_id'];
}
// Walk through the types array, hit the database once for each type of notification that has ids.
foreach($types as $type_id => $type){
if(count($type["ids"]) > 0){
$STH = $this->database->prepare($type["query"] . "( " . implode(",", $type["ids"]) . " )");
$STH->execute();
// Creates a hash table with the primary key as the array key
$types[$type_id]['details'] = $STH->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
$types[$type_id]['details'] = array_map('reset', $types[$type_id]['details']);
// run array_map to make it easier to work with, otherwise it looks like this:
// $results = array(
// 1234 => array(0 => array('username' => 'abc', 'firstname' => '[...]')),
// 1235 => array(0 => array('username' => 'def', 'firstname' => '[...]')),
// );
}
}
// Now walk through notifications again and write out based on notification type,
// referencing $types[<notification type>]["details"][<message id>] for the details
for($i=0; $i< count($notifications); $i++){
// check to see if details for the specific notification exist.
if(isset($types[$notifications[$i]['type']]["details"][$notifications[$i]['feature_id']])){
$notification_details = $types[$notifications[$i]['type']]["details"][$notifications[$i]['feature_id']];
switch ($notifications[$i]['type']) {
case "1":
echo '<li><i>'.timeAgo($notifications[$i]['timestamp']).'</i><a href="user.php?username=' . $notification_details['username'] . '">' . $notification_details['first_name'].'</a> sent you a <a href="inbox.php?message='.$notifications[$i]['feature_id'].'">message</a></li>';
break;
}
}
}
}
更新:添加了在没有详细信息被提取时跳过通知的逻辑(例如消息或用户被删除)
我认为您想运行一个查询,通过连接或更复杂的 where 语句收集所有信息。
选项 1:这可能需要进行调整,以便不是表格的笛卡尔积
SELECT n.id, n.type, m.id, m.body, u.username, u.first_name
FROM notifications n, messages m, users u
WHERE n.user_id = :userid AND m.id = n.feature_id AND u.id = m.sender
选项 2:如果表别名不起作用,则您需要将它们替换为完整的表名
SELECT SELECT n.id, n.type, m.id, m.body, u.username, u.first_name
FROM notifications n
JOIN messages m
ON n.feature_id = m.id
JOIN users u
ON m.sender = u.id
WHERE n.user_id = :userid
罢工><罢工>罢工>
关于php - 优化包含 PDO 查询的 while 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19251690/