我正在使用PostgreSQL 9.3.4在Django 1.6.2应用程序中构建消息传递功能。在用户的"消息"主页,我将显示用户与其他用户的对话列表。每个对话" tile"或块将显示该对话中其他用户的图片和名称,该对话中最后一条消息的发送日期,以及该最后一条消息中的前25个字符。我还会展示一个小小的回复"如果最后一条消息是由正在查看这些对话的用户发送的,则为icon。我已经得到了我的查询,我可以识别查看者和所有其他用户之间的所有对话,但是我无法从用户和消息表中提取我需要的字段。
我的表格(显示在底部)是用户,消息和对话。虽然我已经实现了我的表模式,因此用户和对话之间存在多对多的关系,但在开始时我将创建我的界面,以便用户只能向一个用户发送消息其他用户而不是多个用户。
当我对下面显示的数据运行查询时,我试图找回的是用户3,4,5的对话和用户ID及其关联的用户名,即该对话中的最后一条消息,是谁发送的,以及发送日期。相反,我收到了错误:
ERROR: syntax error at or near "WHERE"
任何人都可以帮我修复此查询吗?我对速度比对优雅更感兴趣。
conversation_user链接表中的数据:
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
我想要回归的行。每条消息都是该对话中的最后一条消息。
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
答案 0 :(得分:2)
基本上,您只需要将WHERE条件移动到适当的位置,例如@Lamak commented:
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;
根据评论:
我试图在用户32正在进行的每个对话中选择最后一条消息。
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;
连接通常比子查询上的IN
构造更快,尤其是对于大集合。但是你也不需要。你一直在过度复杂化。
您可以使用DISTINCT ON
进行更简单的查询,但我希望这一查询更快。
详细说明:
该查询假定(user_id, conversation_id)
为UNIQUE
- 您confirmed in the comment。一定要添加一个实际的UNIQUE约束,它会自动提供所需的索引。
message
(conversation_id, send_date DESC)
上的索引也会有所帮助。详细说明:
假设auth_user.id
是PK,那么它将被编入索引。
message.to_user_uid
可能应该是 to_user_id
- 例如from_user_id
。
您可能希望添加另一个FK以保持一致:
"message_to_user_id_fkey" FOREIGN KEY (to_user_id) REFERENCES auth_user(id)
不确定为什么您认为自己需要DEFERRABLE INITIALLY DEFERRED
。如果您不知道自己需要这个,请将其删除。它用于特殊目的,使常规操作更加昂贵。
如果only two users can take part in the same conversation,完全 删除 conversation_user
并添加user1
和{{}会更有效率1}}或类似于user2
- 除非每个用户/会话组合都有更多属性。也可以简化conversation
。您只需要一个布尔信息,而不是message
和from_user
根据关系理论,to_user
可以被视为表conversation
与其自身之间多对多关系的实现。