这是情况。我有两张桌子:
消息表包含这些列(只是重要的列):
我需要做的是构建一个SELECT查询,它将选择2个用户之间的完整对话。即如果用户A回复用户B发送的消息而用户B回复该消息,我想得到三行:
我确信可以根据reply_to字段构造这样的SELECT查询,但我之前从未做过类似的事情所以我需要一些帮助。
SELECT查询应该是针对MySQL数据库的。
答案 0 :(得分:7)
实际上你是不对的:使用ANSI SQL,不可能。某些具有供应商扩展的数据库(例如Oracle的CONNECT BY
)可能能够执行您想要的操作,但不能使用普通的旧SQL。
我的建议?更改您的数据,以便提供更简单的解决方案。
在这种情况下,给每条消息一个conversation_id。如果用户发布了新消息,请为其指定一个新的(当前未使用的)值。如果他们回复,请保留要回复的邮件的conversation_id。
然后查询数据变得微不足道。
答案 1 :(得分:6)
我建议在conversation_id
表中添加messages
字段。每个新的非回复消息都将生成conversation_id
,然后基于该消息的每个回复将使用相同的ID。然后你的查询很简单:
select * from messages where conversation_id = ? order by id asc
答案 2 :(得分:2)
这是邻接列表模型。
MySQL
没有本地方式来查询它,但你可以使用某个hack:创建一个这样的函数:
CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE _id INT;
DECLARE _parent INT;
DECLARE _next INT;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;
SET _parent = @id;
SET _id = -1;
IF @id IS NULL THEN
RETURN NULL;
END IF;
LOOP
SELECT MIN(id)
INTO @id
FROM messages
WHERE reply_to = _parent
AND id > _id;
IF @id IS NOT NULL OR _parent = @start_with THEN
SET @level = @level + 1;
RETURN @id;
END IF;
SET @level := @level - 1;
SELECT id, reply_to
INTO _id, _parent
FROM messages
WHERE id = _parent;
END LOOP;
END
并在查询中使用它:
SELECT CONCAT(REPEAT(' ', level - 1), CAST(hi.id AS CHAR)) AS treeitem, parent, level
FROM (
SELECT hierarchy_connect_by_parent_eq_prior_id(id) AS id, @level AS level
FROM (
SELECT @start_with := 0,
@id := @start_with,
@level := 0
) vars, messages
WHERE @id IS NOT NULL
) ho
JOIN messages hi
ON hi.id = ho.id
在我的博客中查看此文章,了解有关其工作原理的更详细说明:
只会选择原始邮件的子邮件(其id应用于初始化@start_with
)。
此查询还可以针对sender_id
和receiver_id
的值进行过滤,以确保仅选择用户之间的消息。
答案 3 :(得分:2)
我使用了我在Joe Celko的SQL for Smarties书中找到的一种技术 - 第29章关于树的嵌套集模型。维护数据有点难看(插入,更新,删除),但选择速度非常快。书中的代码非常详尽,并且有很好的解释。还有一些关于如何将您拥有的数据转换为这个新模型的信息。
答案 4 :(得分:1)
使用类似的东西:
SELECT *
FROM [Messages]
WHERE
(
[Sender] = @Sender
AND [Reciever] = @Reciever
) OR (
[Sender] = @Reciever
AND [Reciever] = @Sender)
将为您提供整个对话历史记录。至于reply_to
字段,我不会使用它,因为:
A)检索会话的第一条消息会非常复杂。
B)您可以使用其他过滤器(例如日期)或限制历史记录长度,以防止用户拥有的每个会话的完整输出。
相反,我会在ConversationId
的行中添加一些内容,如果用户之间没有在预先指定的时间内发送消息,则会增加。
在具体回答您的问题时,如果您允许在对话中选择第一条消息,则以下查询将起作用:
SELECT *
FROM [Messages]
WHERE
(
(
[Sender] = @Sender
AND [Reciever] = @Reciever
) OR (
[Sender] = @Reciever
AND [Reciever] = @Sender)
)
AND id >= @FirstMessageId
AND id <
(
SELECT TOP 1 [id]
FROM [Messages]
WHERE [id] > @FirstMessageId
AND [reply_to] IS NULL
AND
(
(
[Sender] = @Sender
AND [Reciever] = @Reciever
) OR (
[Sender] = @Reciever
AND [Reciever] = @Sender
)
)
)