我正在制作类似公告板的东西,要求读者承认他们阅读了它,并且想知道是否有更有效的方法来做这件事。
我在MySQL方面有3个表:
+-----------------+ +-----------------+ +-----------------+
| Announcements | | Acknowledgement | | User |
+-----------------+ +-----------------+ +-----------------+
| announce_id | | ack_id | | user_id |
| announce_msg | | announce_id | | user_name |
| ... | | user_id | | ... |
+-----------------+ +-----------------+ +-----------------+
当用户“读取”公告时(通过单击按钮),将插入Acknowledgment
表,其中包含公告ID和用户ID。当第二个用户“读取”同一个通知时,Acknowledgement
表将再次插入,具有相同的公告ID和第二个用户ID,依此类推......
+--------------------------------+
| Acknowledgement |
+--------+-------------+---------+
| ack_id | announce_id | user_id |
+--------+-------------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 4 |
| 3 | 1 | 3 |
| 4 | 3 | 1 |
| 5 | 3 | 6 |
| 6 | 3 | 2 |
+--------+-------------+---------+
现在问题。在前端,当我列出页面上的所有公告时,我将首先查询所有公告。然后,对于每个公告,我将不得不为已阅读此公告的所有用户执行另一个查询。
$sql = "select * from Announcements";
$result = $pdo->query($sql);
while ($row = $result->fetch())
{
$announce_id = $row['announce_id'];
$announce_msg = $row['annouce_msg'];
$readers = "";
$sql2 = "select u.user_name from Acknowledgement as a INNER JOIN User as u where announcement_id =".$annouce_id;
$result2 = $pdo->query($sql);
while ($row2 = $result2->fetch())
{
$readers .= $row2['user_name'].", ";
}
echo "id:".$annouce_id.", message:".$announce_msg.", Readers:".$readers;
}
因此,如果页面上有10个公告,则每个公告将有10个子查询。我现在所做的工作现在......但是如果有1000个公告怎么办?那么将有1000个子查询?听起来数据库真的会受到重创。所以我希望有更好的方法来做到这一点。
此外,如果用户表中的1000个人读取所有1000个通知,则确认表将具有1000x1000个条目。好像确认表真的很长。随着时间的推移,这会成为一个问题吗?
这是我正在尝试做的一个非常粗略的例子,但它确实需要我很长时间来写这一切。如果需要更多细节,请告诉我。
答案 0 :(得分:3)
有一种更好的方法。您可以对group_concat使用单个查询:
select a.*, group_concat(u.user_name separator ', ') as AllUsers
from Announcements a join
Acknowledgement ak
on a.Announce_Id = ak.Announce_Id join
User u
on u.user_ID = ak.User_ID
group by a.announce_id
这使用隐藏列的MySQL功能仅按一列(announce_id)进行分组,但仍然插入一堆没有聚合的其他列(其他所有内容都被“*”拉入)。
答案 1 :(得分:2)
如果您的目的是过滤掉当前用户已阅读的公告,则可以采用完全不同的方式。而不是查询每个公告,然后查找已阅读这些公告的所有用户并检查那些结果以查找您的用户已阅读并从显示的列表中删除它们的用户,您可以只查询一次性查看特定用户(或用户列表)尚未阅读的所有内容。
将您的查询更改为:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID = <INSERT USER ID HERE>)
这应返回此特定用户尚未确认的所有公告行。如果您将最终WHERE
子句更改为WHERE User_ID IN ()
,则可以指定用户ID列表。
编辑:鉴于您在上面发布的评论,您可以使用此查询来获取所有人都没有阅读的公告:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID IN (SELECT User_ID FROM User))
汇总查询以查找某人(如果不是每个人)都没有阅读的公告的逻辑正在逃避我这一秒。
编辑第二个:每个公告,以及所有已经阅读过的公告,都需要使用您在上面使用的其他类型的联接FULL OUTER JOIN
。不幸的是,MySQL没有那个功能IIRC,但it can be simulated有一个联合查询
SELECT A.*, ACK.*, U.* FROM Announcements AS A
INNER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
LEFT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
WHERE U.User_ID IS NOT NULL
UNION ALL
SELECT A.*, ACK.*, U.* FROM Announcements AS A
LEFT OUTER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
RIGHT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
我认为应该这样做。当然,目前没有可以测试的设施。