我有3个查询。我被告知他们可能效率低下,所以我想知道有经验的人是否可以提出任何建议。 逻辑有些复杂,所以请耐心等待。
我有两个表:shoutbox和主题。主题存储有关所创建主题的所有信息,而shoutbox存储与每个主题相关的所有注释。每条评论都附带一个标有 reply_chunk_id 的组。最早的时间戳是第一个评论,而具有相同 reply_chunk_id 和后续时间戳的任何后续评论都是回复。我想找到由用户启动的每个组的最新评论(发表第一条评论),如果本月发表最新评论,则显示它。
我写的内容实现了一个问题:所有最新评论都以随机顺序显示。我想组织这些小组/最新评论。我非常感谢任何建议
在线留言板
Field Type
-------------------
id int(5)
timestamp int(11)
user varchar(25)
message varchar(2000)
topic_id varchar(35)
reply_chunk_id varchar(35)
主题
id mediumint(8)
topic_id varchar(35)
subject_id mediumint(8)
file_name varchar(35)
topic_title varchar(255)
creator varchar(25)
topic_host varchar(255)
timestamp int(11)
color varchar(10)
mp3 varchar(75)
custom_background varchar(55)
description mediumtext
content_type tinyint(1)
查询
$sql="SELECT reply_chunk_id FROM shoutbox
GROUP BY reply_chunk_id
HAVING count(*) > 1
ORDER BY timestamp DESC ";
$stmt16 = $conn->prepare($sql);
$result=$stmt16->execute();
while($row = $stmt16->fetch(PDO::FETCH_ASSOC)){
$sql="SELECT user,reply_chunk_id, MIN(timestamp) AS grp_timestamp
FROM shoutbox WHERE reply_chunk_id=? AND user=?";
$stmt17 = $conn->prepare($sql);
$result=$stmt17->execute(array($row['reply_chunk_id'],$user));
while($row2 = $stmt17->fetch(PDO::FETCH_ASSOC)){
$sql="SELECT t.topic_title, t.content_type, t.subject_id,
t.creator, t.description, t.topic_host,
c1.message, c1.topic_id, c1.user, c1.timestamp AS max
FROM shoutbox c1
JOIN topic t ON (t.topic_id = c1.topic_id)
WHERE reply_chunk_id = ? AND c1.timestamp > ?
ORDER BY c1.timestamp DESC, c1.id
LIMIT 1";
$stmt18 = $conn->prepare($sql);
$result=$stmt18->execute(array($row2['reply_chunk_id'],$month));
while($row3 = $stmt18->fetch(PDO::FETCH_ASSOC)){
答案 0 :(得分:3)
进行第一次查询:
SELECT reply_chunk_id FROM shoutbox
GROUP BY reply_chunk_id
HAVING count(*) > 1
ORDER BY timestamp DESC
这样做也一样,但速度更快。
确保您在reply_chunk_id
上有索引。
第二个问题:
SELECT user,reply_chunk_id, MIN(timestamp) AS grp_timestamp
FROM shoutbox WHERE reply_chunk_id=? AND user=?
GROUP BY
是不必要的,因为MIN()
和等式测试只返回一行。
第三个问题:
SELECT t.topic_title, t.content_type, t.subject_id,
t.creator, t.description, t.topic_host,
c1.message, c1.topic_id, c1.user, c1.timestamp AS max
FROM shoutbox c1
JOIN topic t ON (t.topic_id = c1.topic_id)
WHERE reply_chunk_id = ? AND c1.timestamp > ?
ORDER BY c1.timestamp DESC, c1.id
LIMIT 1
在一个查询中完成所有操作:
SELECT
t.user,t.reply_chunk_id, MIN(t.timestamp) AS grp_timestamp,
t.topic_title, t.content_type, t.subject_id,
t.creator, t.description, t.topic_host,
c1.message, c1.topic_id, c1.user, c1.timestamp AS max
FROM shoutbox c1
INNER JOIN topic t ON (t.topic_id = c1.topic_id)
LEFT JOIN shoutbox c2 ON (c1.id = c2.id and c1.timestamp < c2.timestamp)
WHERE c2.timestamp IS NULL AND t.user = ?
GROUP BY t.reply_chunk_id
HAVING count(*) > 1
ORDER BY t.reply_chunk_id
或等效的
SELECT
t.user,t.reply_chunk_id, MIN(t.timestamp) AS grp_timestamp,
t.topic_title, t.content_type, t.subject_id,
t.creator, t.description, t.topic_host,
c1.message, c1.topic_id, c1.user, c1.timestamp AS max
FROM shoutbox c1
INNER JOIN topic t ON (t.topic_id = c1.topic_id)
WHERE c1.timestamp = (SELECT max(timestamp) FROM shoutbox c2
WHERE c2.reply_chunk_id = c1.reply_chunk_id)
AND t.user = ?
GROUP BY t.reply_chunk_id
HAVING count(*) > 1
ORDER BY t.reply_chunk_id
这是如何运作的?
topic.reply_chunk_id
(c1.id = c2.id and c1.`timestamp` < c2.`timestamp`) + WHERE c2.`timestamp` IS NULL
仅选择shoutbox中具有最高时间戳的项目。这是因为MySQL继续增加c1.timestamp以使c2.timestamp为null
,只要该为真,它c1.timestamp将达到其最大值并将在可能的行中选择该行以供选择。 如果您不理解第2点,请参阅:http://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
请注意,PDO使用反引号自动转移字段
答案 1 :(得分:2)
听起来大部分应该直接来自你的ShoutBox表。预先查找用户回复的那些块的所有“块”(以及topic_ID,因为每个块总是相同的主题),得到它们各自的最小值和最大值。使用“有计数(*)&gt; 1”将仅强制给定用户进行第二次发布的那些(您正在查找的内容)。
然后,无论用户如何,重新查询块以获得最小值。这可以防止查询所有块的需要。然后,只将单个用户关联的内容加入主题。
此外,我可能不正确,需要调整(最低限度),但似乎SOUNDBOX表ID列将是一个自动增量列,并且恰好在创建时也加盖了时间戳。也就是说,对于给定的“块”,最早的ID将与最早的时间戳相同,因为它们将在创建它们的同时进行标记。也可以使后续的JOIN和子查询更容易。
通过使用STRAIGHT_JOIN,应强制“PreQuery”FIRST,提出一个非常有限的集合,然后限定WHERE子句并加入后缀。
select STRAIGHT_JOIN
T.topic_title,
T.content_type,
T.subject_id,
T.creator,
T.description,
T.topic_host,
sb2.Topic_ID
sb2.message,
sb2.user,
sb2.TimeStamp
from
( select
sb1.Reply_Chunk_ID,
sb1.Topic_ID,
count(*) as TotalEntries,
min( sb1.id ) as FirstIDByChunkByUser,
min( sbJoin.id ) as FirstIDByChunk,
max( sbJoin.id ) as LastIDByChunk,
max( sbJoin.timestamp ) as LastTimeByChunk
from
ShoutBox sb1
join ShoutBox sbJoin
on sb1.Reply_Chunk_ID = sbJoin.Reply_Chunk_ID
where
sb1.user = CurrentUser
group by
sb1.Reply_Chunk_ID,
sb1.Topic_ID
having
min( sb1.id ) = min( sbJoin.ID ) ) PreQuery
join Topic T on
PreQuery.Topic_ID = T.ID
join ShoutBox sb2
PreQuery.LastIDByChunk = sb2.ID
where
sb2.TimeStamp >= YourTimeStampCriteria
order by
sb2.TimeStamp desc
EDIT ---- QUERY EXPLANATION - 使用修改后的查询。 我已经改变了重新阅读的查询(因为在假日周末之后回答时差不多午夜了)。
首先,“STRAIGHT_JOIN”是一个MySQL子句,告诉引擎“以我说过的方式/顺序进行查询”。基本上,有时引擎会尝试为您思考并以可能看起来更高效的方式进行优化,但如果基于您的数据,您知道什么将首先检索最小的数据集,然后加入其他查找字段事实上更好。其次是“PreQuery”。如果你有一个“SQL-Select”语句(在parens中)作为Alias“From”子句,那么“PreQuery”只是结果集别名的名称......我本来可以调用它,只是意味着这个是一个独立的查询。 (Ooops ...固定到ShoutBox :)至于区分大小写,通常列名不区分大小写...但是,表名是......你可以有一个表名“MyTest”不同于“mytest”或“MYTEST”。但是通过提供“别名”,它有助于缩短可读性(特别是使用VeryLongTableNamesUsed)。
在重新阅读并应用调整后应该正常工作。尝试自己的第一个“预查询”以查看它返回的记录数。根据它自身的优点,它应该返回...对于单个“CurrentUser”参数值,每个“Reply_Chunk_ID”(它将始终具有相同的topic_id“,获取该人输入的第一个ID(min())。再次加入对于块ID上的Shoutbox,我们(只有那些符合用户输入的条件),获得每个块的最小和最大ID,无论是谁开始或响应。通过应用HAVING子句,这应该只返回同一个人的那些启动主题(因此两者都具有相同的min()值。)
最后,一旦获得认证,就可以根据他们自己的topic_id和LastIDByChunk的优点直接加入TOPIC和SHOUTBOX表,并按最新的评论响应时间戳降序排序。
我添加了一个where子句,以进一步限制您的“时间戳”条件,其中最近的最终时间戳是在您想要的给定时间段之后/之后。
我很好奇这个查询的时间性能与你已经接受的答案相比如何。