开发者

SQL MAX() in multiple query string

I have a string of 3 queries that are designed to

  1. Find which messages have other messages with the same id which represents replies
  2. Find which messages of the results from the first query have the specified user as entering the first message of that string of messages (min timestamp)
  3. Find the latest message of that string of messages (max timestamp)

The problem comes with the third query. I get the expected results up to the second query, then when the third is executed, without the MAX(timestamp) as max, I get the expected results. When I add that, I only get the first message for each string of messages when it should be the last, regardless of whether I use min or max and the row count says 1 row returned when there is 2 rows shown. Anyone got any ideas on where I went wrong?

$sql="SELECT reply_chunk_id 
        FROM messages 
        GROUP BY reply_chunk_id 
        HAVING count(reply_chunk_id) > 1 ";
$stmt16 = $conn->prepare($sql);
$result=$stmt16->execute(array('specified_user'));
while($row = $stmt16->fetch(PDO::FETCH_ASSOC)){
    $sql="SELECT user,reply_chunk_id, MIN(timestamp) AS grp_timestamp
            FROM messages WHERE reply_chunk_id=?
            GROUP BY reply_chunk_id HAVING user=?";
    $stmt17 = $conn->prepare($sql);
    $result=$stmt17->execute(array($row['reply_chunk_id'],'specified_user'));
    while($row2 = $stmt17->fetch(PDO::FETCH_ASSOC)){
        $sql="SELECT message, MAX(timestamp) as max FROM messages WHERE reply_chunk_id=?";
        $stmt18 = $conn->prepare($sql);
        $result=$stmt18->execute(array($row2['reply_chunk_id']));
        while($row3 = $stmt18->fetch(PDO::FETCH_ASSOC)){
            echo '<p>'.$row3['message'];
        }
    }
}
echo ' '.$stmt18->rowCount();

Create table view of messages, as requested:

CREATE TABLE IF NOT EXISTS `messages` (
  `id` int(5) NOT NULL AUTO_INCREMENT,
  `timestamp` int(11) NOT NULL,
  `use开发者_JS百科r` varchar(25) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'anonimous',
  `message` varchar(2000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `topic_id` varchar(35) NOT NULL,
  `reply_chunk_id` varchar(35) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;


Since message isn't grouped, exactly which message from the group you'll get isn't defined. If you want the message with the maximum timestamp, you'll need to explicitly select it:

SELECT message, timestamp AS max
    FROM messages 
    WHERE reply_chunk_id=:rcid
      AND timestamp=(SELECT MAX(timestamp) 
                         FROM messages 
                         WHERE reply_chunk_id=:rcid)

or:

SELECT message, timestamp AS max
    FROM messages 
    WHERE reply_chunk_id=?
    ORDER BY timestamp DESC, id
    LIMIT 1

The second query breaks ties (in the very unlikely but possible situation that more than one person posts at the same time) by also selecting the message with the highest id.

General feedback

Many of the variables you set within the loops are invariant, and thus should be moved outside the loop.

$stmt17 will return at most 1 result. Moreover, $stmt18 will return always return exactly one result. Rewriting the second inner while loop (for $stmt17) as an if statement, and simply fetching the result from $stmt18 would be equivalent and clearer as to purpose.

try {
  $threadSql="SELECT reply_chunk_id 
          FROM messages 
          GROUP BY reply_chunk_id 
          HAVING count(reply_chunk_id) > 1 ";
  $firstUserSql="SELECT user, MIN(timestamp) AS grp_timestamp
          FROM messages WHERE reply_chunk_id=?
          GROUP BY reply_chunk_id HAVING user=?";
  $lastMsgSql="SELECT message, MAX(timestamp) as max FROM messages WHERE reply_chunk_id=?";
  $threadQuery = $conn->prepare($threadSql);
  $threadQuery->setFetchMode(PDO::FETCH_ASSOC);
  $firstUserQuery = $conn->prepare($firstUserSql);
  $lastMsgQuery = $conn->prepare($lastMsgSql);

  $result=$threadQuery->execute(array('specified_user'));
  foreach ($threadQuery AS $thread){
      $result=$firstUserQuery->execute(array($thread['reply_chunk_id'],'specified_user'));
      if (FALSE !== ($firstUser = $firstUserQuery->fetch(PDO::FETCH_ASSOC))) {
          $result=$lastMsgQuery->execute(array($thread['reply_chunk_id']));
          $lastMsg = $lastMsgQuery->fetch(PDO::FETCH_ASSOC);
          echo '<p>'.$lastMsg['message'].'</p>';
      }
  }
  echo ' ' . $lastMsgQuery->rowCount();
} catch (PDOException $exc) {
  ...
}

Lastly, a single SQL statement can replace much of the PHP code:

SELECT mchunk.reply_chunk_id, 
       muser.user, MIN(muser.`timestamp`) AS grp_timestamp, 
       mmax.message, mmax.`timestamp` AS max
  FROM messages AS mchunk
  JOIN messages AS muser
    ON mchunk.reply_chunk_id = muser.reply_chunk_id
  JOIN messages AS mmax
    ON mchunk.reply_chunk_id = mmax.reply_chunk_id
  WHERE mmax.timestamp=(SELECT MAX(timestamp) FROM messages AS m WHERE m.reply_chunk_id=mchunk.reply_chunk_id)
  GROUP BY mchunk.reply_chunk_id, muser.user
  HAVING count(mchunk.reply_chunk_id) > 1
    AND muser.user IN ('steve', '0010')
;

This selects all threads started by a specified user that have responses, along with the most recent response.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜