需要有关复杂嵌套查询的专家建议

时间:2011-05-30 20:39:04

标签: php mysql sql

我有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)){

2 个答案:

答案 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

这是如何运作的?

  1. 每个topic.reply_chunk_id
  2. 选择一个条目
  3. 左连接(c1.id = c2.id and c1.`timestamp` < c2.`timestamp`) + WHERE c2.`timestamp` IS NULL仅选择shoutbox中具有最高时间戳的项目。这是因为MySQL继续增加c1.timestamp以使c2.timestamp为null,只要该为真,它c1.timestamp将达到其最大值并将在可能的行中选择该行以供选择。
  4. 如果您不理解第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子句,以进一步限制您的“时间戳”条件,其中最近的最终时间戳是在您想要的给定时间段之后/之后。

我很好奇这个查询的时间性能与你已经接受的答案相比如何。