消息传递应用程序的复杂SQL查询
我正在使用PostgreSQL 9.3.4在Django 1.6.2应用程序中构建消息传递功能。在用户的“消息”主页上,我将显示一个用户与其他用户的对话列表。每个对话“平铺”或区块将显示该对话中其他用户的图片和姓名、该对话中最后一条消息的发送日期以及最后一条消息中的前25个字符。如果最后一条消息是由查看这些对话的用户发送的,我还将显示一个小的“回复”图标。我的查询已经到了可以识别查看器和所有其他用户之间的所有对话的程度,但是从用户表和消息表中提取所需字段时遇到了问题 我的表格(显示在底部)包括用户、消息和对话。虽然我已经实现了我的表模式,以便在用户和对话之间存在多对多关系,但在开始时,我将创建我的界面,以便一个用户只能向另一个用户而不是多个用户发送消息 当我对下面显示的数据运行查询时,我试图获取的是用户3、4、5的对话和用户ID,以及他们的相关用户名、对话中的最后一条消息、发送者和发送日期。相反,我得到了一个错误: 有人能帮我解决这个问题吗?我对速度比对优雅更感兴趣 测试用例 对话\用户链接表中的数据:消息传递应用程序的复杂SQL查询,sql,django,postgresql,database-design,greatest-n-per-group,Sql,Django,Postgresql,Database Design,Greatest N Per Group,我正在使用PostgreSQL 9.3.4在Django 1.6.2应用程序中构建消息传递功能。在用户的“消息”主页上,我将显示一个用户与其他用户的对话列表。每个对话“平铺”或区块将显示该对话中其他用户的图片和姓名、该对话中最后一条消息的发送日期以及最后一条消息中的前25个字符。如果最后一条消息是由查看这些对话的用户发送的,我还将显示一个小的“回复”图标。我的查询已经到了可以识别查看器和所有其他用户之间的所有对话的程度,但是从用户表和消息表中提取所需字段时遇到了问题 我的表格(显示在底部)包括用
id | conversation_id | user_id
----+-----------------+---------
1 | 1 | 32
2 | 1 | 3 <- want this
3 | 2 | 32
4 | 2 | 4 <- want this
6 | 3 | 3
7 | 3 | 1
8 | 4 | 32
9 | 4 | 5 <- want this
10 | 5 | 7
11 | 5 | 9
表定义
固定语法
基本上,您只需要将WHERE条件移动到适当的位置,如:
笔记
- 联接通常比子查询中的构造中的更快,尤其是对于大集合。但你也不需要。你把事情搞得太复杂了
- 您可以使用上的
进行更简单的查询,但我希望这个查询会更快。
详情:
- 该查询假定
是唯一的(用户id,会话id)
,您可以使用它。请确保添加一个实际的唯一约束,它会自动提供急需的索引
- 在
上的(对话id,发送日期描述)
上建立索引也会有所帮助。详情:消息
- 假设
是主键,那么它将被索引auth_user.id
可能应该是message.to\u user\u uid
-就像来自\u user\u id的to\u user\u id
- 您可能希望添加另一个FK以保持一致:
不确定您为什么认为需要"message_to_user_id_fkey" FOREIGN KEY (to_user_id) REFERENCES auth_user(id)
。如果你不知道你需要这个,把它拿走。这是为了特殊目的,使常规操作更加昂贵可延迟的初始延迟
- 如果是,将删除
并添加对话\u user
和user1
或类似于user2
——除非每个用户/对话组合都有更多属性。也可能简化对话
。您只需要一个布尔信息,而不需要从\u user和消息
到\u user
根据关系理论,
可以看作是表conversation
与自身之间多对多关系的实现auth_user
ORDER BY
移动到WHERE
条件之后:WHERE conversation\u id。。。。。ORDER BY m.send_date DESC LIMIT 1
为什么要编写原始SQL而不是使用Django模型层?@DanielRoseman,我使用原始SQL是因为尽管我不是SQL专家,但编写SQL比使用Django的ORM命令要舒服得多。此外,我还想消除Django必须将ORM命令转换为SQL可能导致的任何性能开销。@DanielRoseman:使用Django模型层有什么好处?@Lamak,谢谢你的输入,但我不得不感谢Erwin,因为他为我提供了大量帮助。Erwin,非常感谢。这几乎就是我想要做的。我正在尝试选择user32正在进行的三次对话(与用户3、4和5)中的最后一条消息。这只给了我user32和user5之间最后一次对话中的最后一条消息。我将继续研究这个问题,看看是否可以解决它。如果您知道需要进行哪些更改,请告诉我。再次感谢!这实际上检索所需的行;它只是缺少用户表和消息表中的列。它是从你之前帮助我的一个对话中派生出来的。@Robert:有多少用户可以参加同一个对话?但是,每个用户只能使用一次?一个唯一的约束将强制执行这一点(并自动创建提到的索引)。哦,不,每个对话只能有两个用户参与。user32和user3可以有一个对话,user32和user4可以有一个单独的对话,但是用户32、3和4不能都在同一个对话中。在每次对话中,两个用户中的每一个都可以向另一个用户发送任意数量的消息。我刚刚编写了一个查询,它使用conversation\u id选择消息字段,因此几乎可以看出,根据user\u id选择对话并生成conversation\u id的查询需要是消息表查询的子查询。我正试图弄明白这一点。从概念上讲,它需要像这样工作,只是这个查询不会执行。子查询只能选择一列,而我的子查询选择四列。
conversation_id | user_id | username | from_user | message | send_date
----------------+---------+-----------+-----------+---------+----------
1 | 3 | user3 | u3 or u32 | <msg3> | <date>
2 | 4 | user4 | u4 or u32 | <msg4> | <date>
4 | 5 | user5 | u5 or u32 | <msg5> | <date>
SELECT cu.conversation_id,
cu.user_id,
au.username,
m.from_user,
m.message,
m.send_date
FROM conversation_user cu
INNER JOIN auth_user au ON cu.user_id = au.id
INNER JOIN message m ON cu.conversation_id = m.conversation_id
ORDER BY m.send_date DESC LIMIT 1
WHERE conversation_id IN
(SELECT conversation_id
FROM conversation_user
WHERE user_id = 32)
AND user_id != 32;
# auth_user
--------------+--------------------------+------------------------------
id | integer | not null default nextval(...
username | character varying(30) | not null
Referenced by:
TABLE "conversation_user" CONSTRAINT "conversation_user_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
TABLE "message" CONSTRAINT "message_from_user_id_fkey" FOREIGN KEY (from_user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
# conversation
------------+--------------------------+--------------------------------
id | integer | not null default nextval(...
start_date | timestamp with time zone | not null
Referenced by:
TABLE "conversation_user" CONSTRAINT "conversation_id_refs_id_4344ca71" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
TABLE "message" CONSTRAINT "message_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
# conversation_user
-----------------+---------+--------------------------------------------
id | integer | not null default nextval(...
conversation_id | integer | not null
user_id | integer | not null
Foreign-key constraints:
"conversation_id_refs_id_4344ca71" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
"conversation_user_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
# message
Column | Type |
-----------------+--------------------------+---------------------------
id | integer | not null default nextval(...
conversation_id | integer | not null
from_user_id | integer | not null
to_user_uid | integer | not null
message | text | not null
send_date | timestamp with time zone | not null
Foreign-key constraints:
"message_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
"message_from_user_id_fkey" FOREIGN KEY (from_user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
SELECT ...
FROM conversation_user cu
INNER JOIN ...
WHERE conversation_id IN
(SELECT conversation_id
FROM conversation_user
WHERE user_id = 32)
AND user_id != 32
ORDER BY m.send_date DESC
LIMIT 1;
SELECT cu.conversation_id
, ufrom.username AS from_user
, uto.username AS to_user
, m.message
, m.send_date
FROM conversation_user cu
LEFT JOIN LATERAL (
SELECT from_user_id, to_user_id, message, send_date
FROM message m
WHERE m.conversation_id = cu.conversation_id
ORDER BY send_date DESC
LIMIT 1
) m ON TRUE
LEFT JOIN auth_user ufrom ON ufrom.id = m.from_user_id
LEFT JOIN auth_user uto ON uto.id = m.to_user_id
WHERE cu.user_id = 32;
"message_to_user_id_fkey" FOREIGN KEY (to_user_id) REFERENCES auth_user(id)