简体   繁体   English

消息传递应用程序的复杂SQL查询

[英]Complex SQL query for messaging app

I'm building a messaging feature in a Django 1.6.2 application using PostgreSQL 9.3.4. 我正在使用PostgreSQL 9.3.4在Django 1.6.2应用程序中构建消息传递功能。 On a user's "Messages" home page, I'll show a list of conversations that a user has with other users. 在用户的“消息”主页上,我将显示一个用户与其他用户的对话列表。 Each conversation "tile" or block will show the picture and name of the other user in that conversation, the date the last message in that conversation was sent, and the first 25 characters in that last message. 每个对话“平铺”或块将显示该对话中另一个用户的图片和名称,该对话中最后一条消息的发送日期以及该最后一条消息中的前25个字符。 I'll also show a small "reply" icon if the last message was sent by user who is viewing these conversations. 如果最后一条消息是由正在查看这些对话的用户发送的,我还将显示一个小的“答复”图标。 I've got my query to the point where I can identify all of the conversations between the viewer and all the other users but I'm having trouble pulling in the fields I need from the User and Message tables. 我已经查询到可以识别查看器与所有其他用户之间的所有对话的地步,但是我在从“用户”和“消息”表中提取所需字段时遇到了麻烦。

My tables (shown at the bottom) are User, Message, and Conversation. 我的表格(显示在底部)是用户,消息和对话。 Although I've implemented my table schemas so that there is a many-to-many relationship between users and conversations, in the beginning I'm going to create my interface so that a user can only send a message to one other user instead of multiple users. 尽管我已经实现了表模式,以便用户和会话之间存在多对多关系,但一开始我将创建我的界面,以便用户只能向其他用户发送消息,而不能多个用户。

When I run my query on the data shown below, what I'm trying to get back is the conversation and user IDs for users 3, 4, 5 along with their associated usernames, the last message in that conversation, who sent it, and the date it was sent. 当我对下面显示的数据运行查询时,我要获取的是用户3、4、5的对话和用户ID以及相关的用户名,对话中的最后一条消息,发送者以及发送日期。 Instead, I'm getting the error: 相反,我得到了错误:

 ERROR: syntax error at or near "WHERE" 

Can anyone help me fix this query? 谁能帮我解决这个问题? I'm more interested in speed than elegance. 我对速度比对优雅更感兴趣。

Test case 测试用例

Data in conversation_user linking table: 对话用户链接表中的数据:

 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

Rows I want to return. 我要返回的行。 Each message is last message in that conversation. 每个消息是该会话中的最后一条消息。

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>

Query that isn't working: 查询无效:

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;

Table definitions 表定义

# 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

Fix syntax 修正语法

Basically, you just need to move the WHERE condition to its proper place, like @Lamak commented : 基本上,您只需要将WHERE条件移动到适当的位置,如@Lamak注释

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;

Make it fast 快一点

According to comment: 根据评论:

I'm trying to select the last message in each of the [...] conversations user 32 is having. 我正在尝试选择用户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;

Notes 笔记

DB design 数据库设计

  • The query assumes that (user_id, conversation_id) is UNIQUE - which you confirmed in the comment . 该查询假定(user_id, conversation_id)UNIQUE已在注释中确认了这一点 Be sure to add an actual UNIQUE constraint, which provides the much needed index automatically. 确保添加实际的UNIQUE约束,该约束会自动提供急需的索引。

  • An index on message on (conversation_id, send_date DESC) would help, too. message上的索引(conversation_id, send_date DESC)也将有所帮助。 Details: 细节:

  • Assuming auth_user.id is the PK, so it would be indexed. 假设auth_user.id是PK,那么它将被索引。

  • message.to_user_uid is probably supposed to be to_user_id - like from_user_id . message.to_user_uid可能应该是to_user_id ,例如from_user_id

  • You probably want to add another FK to stay consistent: 您可能想要添加另一个FK来保持一致:

     "message_to_user_id_fkey" FOREIGN KEY (to_user_id) REFERENCES auth_user(id) 

    Not sure why you think you need DEFERRABLE INITIALLY DEFERRED . 不知道为什么您认为自己需要DEFERRABLE INITIALLY DEFERRED If you don't know you need this, remove it. 如果您不知道需要此功能,请将其删除。 It's for special purposes and makes regular operations more expensive. 这是出于特殊目的,使常规操作的成本更高。

  • If only two users can take part in the same conversation , it would be more efficient to remove conversation_user altogether and add user1 and user2 or similar to conversation - unless there are more attributes for each combination of user/conversation. 如果只有两个用户可以参加同一对话 ,则完全删除 conversation_user user1并添加user1user2或类似于conversation将更为有效-除非用户/对话的每种组合都有更多的属性。 Potentially simplify message , too. 也可能简化message You only need a boolean information instead of from_user and to_user . 您只需要布尔信息,而不是from_userto_user
    According to relational theory, conversation can be seen as a the implementation of many-to-many relationship between table auth_user and itself. 根据关系理论, conversation可以看作是表auth_user与自身之间多对多关系的一种实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM