MySQL JOIN 查询 - 消息系统

发布于 2024-11-07 21:03:30 字数 1736 浏览 1 评论 0原文

我有一个消息系统的下表,我想知道如何查询数据库以了解有多少对话有新消息。

我的表如下

Conversation
------------
id
subject

Messages
--------
id
conversation_id
user_id (sender)
message
timestamp (time sent)

Participants
------------
conversation_id
user_id
last_read (time stamp of last view user viewed conversation)

我正在尝试执行以下查询,但它没有返回结果:

SELECT COUNT(m.conversation_id) AS count
FROM (messages_message m)
INNER JOIN messages_participants p ON p.conversation_id = m.conversation_id
WHERE `m`.`timestamp` > 'p.last_read'
AND `p`.`user_id` = '5'
GROUP BY m.conversation_id
LIMIT 1 

另外,我可能必须在每个页面加载时运行此查询 - 有任何使其尽可能快的提示吗?

干杯

编辑

我还有另一个有点相关的问题,如果有人愿意帮忙的话。

我正在尝试检索主题、对话中的最后一条消息、上次对话的时间戳和新消息的数量。我相信我有一个有效的查询,但看起来组合起来有点糟糕。我可以对此做哪些改进?

SELECT SQL_CALC_FOUND_ROWS c.*, last_msg.*, new_msgs.count as new_msgs_count 
                        FROM ( messages_conversation c ) 
                        INNER JOIN messages_participants p ON p.user_id = '5' 
                        INNER JOIN ( SELECT m.* 
                                    FROM (messages_message m) 
                                    ORDER BY m.timestamp DESC 
                                    LIMIT 1) last_msg 
                                ON c.id = last_msg.conversation_id 
                        LEFT JOIN ( SELECT COUNT(m.id) AS count, m.conversation_id, m.timestamp  
                                    FROM (messages_message m) ) new_msgs 
                                ON c.id = new_msgs.conversation_id AND new_msgs.timestamp > p.last_read 
                        LIMIT 0,10

我应该通过在 MySQL 中执行 IF 语句来确定对话是否未读,还是应该在 PHP 上转换和比较时间戳?

再次感谢, RS7

I have the following tables for a messaging system and I was wondering how I would go about querying the DB for how many conversations have new messages.

My tables are as follows

Conversation
------------
id
subject

Messages
--------
id
conversation_id
user_id (sender)
message
timestamp (time sent)

Participants
------------
conversation_id
user_id
last_read (time stamp of last view user viewed conversation)

I'm trying to do the following query but it returns no results:

SELECT COUNT(m.conversation_id) AS count
FROM (messages_message m)
INNER JOIN messages_participants p ON p.conversation_id = m.conversation_id
WHERE `m`.`timestamp` > 'p.last_read'
AND `p`.`user_id` = '5'
GROUP BY m.conversation_id
LIMIT 1 

Also, I probably will have to run this on every page load - any tips of making it as fast as possible?

Cheers

EDIT

I've got another somewhat related question if anybody would be so kind as to help out.

I'm trying to retrieve the subject, last message in conversation, timestamp of last convo and number of new messages. I believe I have a working query but it looks a bit badly put together. What sort of improvements can I do to this?

SELECT SQL_CALC_FOUND_ROWS c.*, last_msg.*, new_msgs.count as new_msgs_count 
                        FROM ( messages_conversation c ) 
                        INNER JOIN messages_participants p ON p.user_id = '5' 
                        INNER JOIN ( SELECT m.* 
                                    FROM (messages_message m) 
                                    ORDER BY m.timestamp DESC 
                                    LIMIT 1) last_msg 
                                ON c.id = last_msg.conversation_id 
                        LEFT JOIN ( SELECT COUNT(m.id) AS count, m.conversation_id, m.timestamp  
                                    FROM (messages_message m) ) new_msgs 
                                ON c.id = new_msgs.conversation_id AND new_msgs.timestamp > p.last_read 
                        LIMIT 0,10

Should I determine if the conversations is unread by doing an IF statement in MySQL or should I convert and compare timestamps on PHP?

Thanks again,
RS7

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

错爱 2024-11-14 21:03:30

上面引用的 'p.last_read' 是一个字符串常量 - 删除其中的引号,看看是否会改变任何东西,RS7。如果 user_id 是整数,则还要删除 '5' 中的引号。

就性能而言,请确保所有相关列都有索引。 messages_participants.user_id 和 messages_message.timestamp 是要索引的两个重要列。

'p.last_read' as quoted above is a string constant - remove the quotes from this and see whether that changes anything, RS7. If user_id is an integer than remove the quotes from '5' as well.

As far as performance goes, ensure you have indexes on all the relevant columns. messages_participants.user_id and messages_message.timestamp being two important columns to index.

蒲公英的约定 2024-11-14 21:03:30

是的,您的查询有问题。

首先,您应该注意到您对正在分组的列进行了计数,因此计数结果将为 1。
其次,您将时间戳与字符串进行比较: m.timestamp > 'p.last_read'
最后,当您知道查询将返回一行时,请避免使用 LIMIT(自信点:p)。

尝试:

SELECT
  COUNT(m.conversation_id) AS count
FROM
  messages_message m
INNER JOIN
  messages_participants p ON p.conversation_id = m.conversation_id
WHERE
  m.timestamp > p.last_read
  AND p.user_id = 5

如果您想增加查询运行时间,您可以在 message_participants (conversation_id, user_id) 中创建一个新索引来索引每个用户的对话,然后更改您的查询:

SELECT
  COUNT(m.conversation_id) AS count
FROM
  messages_message m
INNER JOIN
  messages_participants p ON p.conversation_id = m.conversation_id AND p.user_id = 5
WHERE
  m.timestamp > p.last_read

以便您的数据库引擎现在可以通过简单地查找来过滤 JOIN在索引表中。您也可以通过对时间戳进行索引来更深入地思考这一点:(timestamp,conversation_id,user_id)并将where条件放入连接条件中。

无论您选择什么,始终将最具选择性的领域放在第一位,以提高选择性。

编辑

首先,让我们评论您的查询:

SELECT
  SQL_CALC_FOUND_ROWS c.*,
  last_msg.*,
  new_msgs.count as new_msgs_count
FROM
  messages_conversation c
INNER JOIN
  messages_participants p ON p.user_id = 5 -- Join with every conversations of user 5; if id is an integer, avoid writing '5' (string converted to an integer).
INNER JOIN
( -- Select every message : you could already select here messages from user 5
  SELECT
    * 
  FROM
    messages_message m
  ORDER BY -- this is not the goal of ORDER BY. Use MAX to obtain to latest timestamp.
    m.timestamp DESC 
  LIMIT 1
) last_msg ON c.id = last_msg.conversation_id  -- this query return one row and you want to have the latest timestamp for each conversation.
LEFT JOIN
(
  SELECT
    COUNT(m.id) AS count,
    m.conversation_id,
    m.timestamp  
  FROM
    messages_message m
) new_msgs ON c.id = new_msgs.conversation_id AND new_msgs.timestamp > p.last_read 
LIMIT 0,10

让我们重新表述您的查询:
选择对话主题的新消息数、最后一条消息以及用户@id 的时间戳。

逐步执行:

为每个用户选择最后一条消息、对话中的时间戳:

SELECT -- select the latest timestamp with its message
  max(timestamp),
  message
FROM
  messages_message
GROUP BY
  user_id

聚合函数(MAX、MIN、SUM,...)适用于当前组。读起来就像“对于每个组,计算聚合函数,然后在条件成立的情况下选择我需要的”。因此,这将导致每组一行。
因此,最后一个查询选择 messages_message 表中每个用户的最后一条消息和时间戳。正如您所看到的,通过添加 WHERE 子句,可以轻松为特定用户选择此值:

SELECT
  MAX(timestamp),
  message
FROM
  messages_message
WHERE
  user_id = @id
GROUP BY
  user_id

每个对话的消息数:对于每个对话,计算消息数

SELECT
  COUNT(m.id) -- assuming id column is unique, otherwise count distinct value.
FROM
  messages_conversation c
INNER JOIN -- The current user participated to the conversation
  messages_participant p ON p.conversation_id = c.id AND p.user_id = @id
OUTER JOIN -- Messages of the conversation where the current user participated, newer than last read its time
  messages_message m ON m.conversation_id = c.id AND m.timestamp > p.last_read = @id
GROUP BY
  c.id -- for each conversation

INNER JOIN 不会返回行用于当前用户未参与的对话。
然后,如果条件为 false,OUTER JOIN 将与 NULL 列连接,以便 COUNT 将返回 0 - 没有新消息。

把它们放在一起。

选择当前用户参与的对话中的最后一条消息和时间戳以及每个对话中的新消息数。
这是最后两个查询之间的 JOIN。

SELECT
   last_msg.conversation_id,
   last_msg.message,
   last_msg.max_timestamp,
   new_msgs.nb
FROM
(
  SELECT
    MAX(timestamp)   AS max_timestamp,
    message,
    conversation_id
  FROM
    messages_message
  WHERE
    user_id = @id
  GROUP BY
    user_id
) last_msg
JOIN
(
  SELECT
    c.id         AS conversation_id
    COUNT(m.id)  AS nb
  FROM
    messages_conversation c
  INNER JOIN
    messages_participant p ON p.conversation_id = c.id AND p.user_id = @id
  OUTER JOIN
    messages_message m ON m.conversation_id = c.id AND m.timestamp > p.last_read = @id
  GROUP BY
    C.id
) new_msgs ON new_msgs.conversation_id = last_msg.conversation_id
-- put here and only here a order by if necessary :)

Yes, you have problem in your query.

Firstly, you should have noticed that you count the column you are grouping, so the count result will be 1.
Secondly, you are comparing the timestamp to a string : m.timestamp > 'p.last_read'.
Finally, avoid using LIMIT when you know your query will return one row (be self-confident :p).

Try:

SELECT
  COUNT(m.conversation_id) AS count
FROM
  messages_message m
INNER JOIN
  messages_participants p ON p.conversation_id = m.conversation_id
WHERE
  m.timestamp > p.last_read
  AND p.user_id = 5

if you want to increase the query running time you can create a new index in message_participants (conversation_id, user_id) to index the conversations per users and then change your query with:

SELECT
  COUNT(m.conversation_id) AS count
FROM
  messages_message m
INNER JOIN
  messages_participants p ON p.conversation_id = m.conversation_id AND p.user_id = 5
WHERE
  m.timestamp > p.last_read

So that your DB engine can now filter the JOIN by simply looking at the index table. You could go deeper in this thought by indexing the timestampe too : (timestamp, conversation_id, user_id) and put the where condition in the join condition.

Whatever you choose, always put the most selective field first, to increase selectivity.

EDIT

First, let's comment your query:

SELECT
  SQL_CALC_FOUND_ROWS c.*,
  last_msg.*,
  new_msgs.count as new_msgs_count
FROM
  messages_conversation c
INNER JOIN
  messages_participants p ON p.user_id = 5 -- Join with every conversations of user 5; if id is an integer, avoid writing '5' (string converted to an integer).
INNER JOIN
( -- Select every message : you could already select here messages from user 5
  SELECT
    * 
  FROM
    messages_message m
  ORDER BY -- this is not the goal of ORDER BY. Use MAX to obtain to latest timestamp.
    m.timestamp DESC 
  LIMIT 1
) last_msg ON c.id = last_msg.conversation_id  -- this query return one row and you want to have the latest timestamp for each conversation.
LEFT JOIN
(
  SELECT
    COUNT(m.id) AS count,
    m.conversation_id,
    m.timestamp  
  FROM
    messages_message m
) new_msgs ON c.id = new_msgs.conversation_id AND new_msgs.timestamp > p.last_read 
LIMIT 0,10

Let's rephrase your query:
select the number of new messages of a conversation subject, its last message and timestamp for user @id.

Do it step by step:

Selecting last message, timestamp in conversation for each user:

SELECT -- select the latest timestamp with its message
  max(timestamp),
  message
FROM
  messages_message
GROUP BY
  user_id

Aggregates functions (MAX, MIN, SUM, ...) work on the current group. Read this like "for each groups, calculate the aggregate functions, then select what I need where my conditions are true". So it will result in one row per group.
So this last query selects the last message and timestamp of every user in the messages_message table. As you can see, it is easy to select this value for a specific user adding the WHERE clause:

SELECT
  MAX(timestamp),
  message
FROM
  messages_message
WHERE
  user_id = @id
GROUP BY
  user_id

Number of messages per conversation: for each conversation, count the number of messages

SELECT
  COUNT(m.id) -- assuming id column is unique, otherwise count distinct value.
FROM
  messages_conversation c
INNER JOIN -- The current user participated to the conversation
  messages_participant p ON p.conversation_id = c.id AND p.user_id = @id
OUTER JOIN -- Messages of the conversation where the current user participated, newer than last read its time
  messages_message m ON m.conversation_id = c.id AND m.timestamp > p.last_read = @id
GROUP BY
  c.id -- for each conversation

INNER JOIN won't return rows for conversations where the current user did not participated.
Then OUTER JOIN will join with NULL columns if the condition is false, so that COUNT will return 0 - there is not new messages.

Putting it all together.

Select the last message and timestamp in conversation where the current user participated and the number of new messages in each conversation.
Which is a JOIN between the two last queries.

SELECT
   last_msg.conversation_id,
   last_msg.message,
   last_msg.max_timestamp,
   new_msgs.nb
FROM
(
  SELECT
    MAX(timestamp)   AS max_timestamp,
    message,
    conversation_id
  FROM
    messages_message
  WHERE
    user_id = @id
  GROUP BY
    user_id
) last_msg
JOIN
(
  SELECT
    c.id         AS conversation_id
    COUNT(m.id)  AS nb
  FROM
    messages_conversation c
  INNER JOIN
    messages_participant p ON p.conversation_id = c.id AND p.user_id = @id
  OUTER JOIN
    messages_message m ON m.conversation_id = c.id AND m.timestamp > p.last_read = @id
  GROUP BY
    C.id
) new_msgs ON new_msgs.conversation_id = last_msg.conversation_id
-- put here and only here a order by if necessary :)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文