更好/正确的方式来编写复杂的查询

时间:2015-02-13 21:35:30

标签: sql sql-server sqlite

这是我的桌子。

SELECT * FROM [Message]

Message Table Data

现在我想要的是,我想要从其他任何用户发送或接收的用户 Id:101 的最后一条消息列表。我为它写的查询在

之下
SELECT
(SELECT TOP 1 [Message_id]
 FROM [Message]
 WHERE
 ([Sender_id] = REC.[Sender_id] AND [Receiver_id] = REC.[Receiver_id])
 OR
 ([Sender_id] = REC.[Receiver_id] AND [Receiver_id] = REC.[Sender_id])
 ORDER BY 1 DESC) AS [Message Id],
REC.[Sender_id] AS [Sender Id],
REC.[Receiver_id] AS [Receiver Id],
(SELECT TOP 1 [Message]
 FROM [Message]
 WHERE
 ([Sender_id] = REC.[Sender_id] AND [Receiver_id] = REC.[Receiver_id])
 OR
 ([Sender_id] = REC.[Receiver_id] AND [Receiver_id] = REC.[Sender_id])
 ORDER BY 1 DESC) AS [Message]
FROM
(SELECT DISTINCT [Sender_id], [Receiver_id]
 FROM [Message]
 WHERE [Sender_id] = '101') REC

我得到的结果似乎很好。

Query Results

我是数据库查询的新手,似乎我的查询非常低效且冗长。任何人都可以建议一个更好的方式来编写这个查询?此外,使用 JOINS ,如果这可能是更好的方式来编写此查询。

注意:请将Message_id视为唯一编号,而不是有序标识列,在实际情况下可能是任何生成的唯一字母数字代码。

感谢。

3 个答案:

答案 0 :(得分:4)

这应该可以解决问题。

WITH Priorities AS (
   SELECT
      Priority = Row_Number() OVER (PARTITION BY X.Party2 ORDER BY Message_id DESC),
      M.*
   FROM
      dbo.Message M
      OUTER APPLY (VALUES
         (M.Sender_id, M.Receiver_Id),
         (M.Receiver_id, M.Sender_Id)
      ) X (Party1, Party2)
   WHERE
      Party1 = '101'
)
SELECT *
FROM Priorities
WHERE Priority = 1     
;

See this working in a Sql Fiddle

说明:

真正的问题是,您不关心所选人是发件人还是接收者。这会导致处理从一列或另一列中提取值的复杂性,例如可以使用CASE语句以典型方式解决。

但是,我总是喜欢在关系上而不是在程序上解决这些事情,所以我通过(基本上)加倍数据来简化问题。对于表格中的每个源行,我们将生成两行,一行是发件人首先出现的行,另一行是接收者出现的行。我们不关心哪一个是发件人或收件人,我们可以说我们正在寻找Party1101,然后想要找到每个人Party2他交换了一条消息(无论他是发送者还是接收者都是无关紧要的),是最新的消息。

OUTER APPLY只是让我们避免做更多CTE或嵌套查询(另一种编写方式)的技巧。它就像LEFT JOIN,但让我们使用外部引用(它引用表M中的列)。

对于它的价值,Message_id似乎不是选择最新消息的可靠方式。您应该有一个日期列,然后按顺序排列! (只需在您的表格中添加datetimedatetime2列,然后更改ORDER BY即可使用它。您永远不知道是否可以将消息从实际发生时的顺序插入到表中实际上应该是它们出现故障了。如果你不得不重新插入丢失的消息怎么办?根据我的经验,标识列不是保证插入顺序的好方法。

P.S。我最初的想法是,您希望为每个发件人和收件人发送收到的最新邮件的最新邮件。但是,这不是你要求的。我以为我会把它留给子孙后代,因为它对某人来说也是一个有用的答案:

WITH Priorities AS (
   SELECT
      SNum = Row_Number() OVER (PARTITION BY Sender_id ORDER BY Message_id DESC),
      PNum = Row_Number() OVER (PARTITION BY Receiver_id ORDER BY Message_id DESC),
      *
   FROM
      Message
   WHERE
      '101' IN (Sender_id, Receiver_id)
)
SELECT *
FROM Priorities
WHERE 1 IN (SNum, PNum)
;

答案 1 :(得分:3)

如果您希望向/从其他用户发送最新消息,请计算其他用户作为发件人和收件人的新近度(基于message_id)。诀窍是使用其他用户作为分区键进行分区。

然后在外部查询中选择第一个:

select m.*
from (select m.*,
             row_number() over (partition by (case when sender_id = 101 then receiver_id else sender_id end)
                                order by message_id desc) as seqnum
      from message m
      where 101 in (sender_id, receiver_id)
     ) m
where seqnum = 1;

答案 2 :(得分:0)

希望这有帮助

SELECT * FROM (
SELECT Message_id, Sender_id, Receiver_id, Message, RANK() OVER(PARTITION BY Sender_id, Receiver_id ORDER BY Message_id DESC) OBD FROM Message
WHERE Sender_id = 101 OR Receiver_id = 101) A WHERE A.OBD = 1
相关问题