7

次のようなメッセージテーブルがあります。

                    Messages
+-----+------------+-------------+--------------+
|  id |  sender_id | receiver_id |  created_at  |
+-----------------------------------------------+
|  1  |      1     |      2      |   1/1/2013   |
|  2  |      1     |      2      |   1/1/2013   |
|  3  |      2     |      1      |   1/2/2013   |
|  4  |      3     |      2      |   1/2/2013   |
|  5  |      3     |      2      |   1/3/2013   |
|  6  |      5     |      4      |   1/4/2013   |
+-----------------------------------------------+

'thread'が特定のsender_idとreceiver_idの間のメッセージのグループである場合、sender_idまたはreceiver_idのいずれかが特定のIDである最新の10スレッドの最新の10メッセージを返すクエリが必要です。

指定されたuser_idが5の場合に期待される出力:

+-----+------------+-------------+--------------+
|  id |  sender_id | receiver_id |  created_at  |
+-----------------------------------------------+
|  1  |      5     |      2      |   1/4/2013   |
|  2  |      5     |      2      |   1/4/2013   |
|  3  |      2     |      5      |   1/4/2013   |
|  4  |      3     |      5      |   1/4/2013   |
|  5  |      5     |      2      |   1/3/2013   |
|  6  |      5     |      4      |   1/3/2013   |
+-----------------------------------------------+

たとえば、ユーザー5と2の間の最大10メッセージ(4つ以上)と10スレッドの制限(3つ以上)。

私はサブクエリを使用してこの種のクエリを試してきましたが、個別のスレッドの数の2番目の制限を取得できませんでした。

SELECT * FROM (SELECT DISTINCT ON (sender_id, receiver_id) messages.* 
FROM messages 
WHERE (receiver_id = 5 OR sender_id = 5) ORDER BY sender_id, receiver_id, 
created_at DESC)   
q ORDER BY created_at DESC 
LIMIT 10 OFFSET 0;

sender_id + Receiver_idを連結したthread_idフィールドを含む新しいThreadテーブルを作成し、Messagesに参加することを検討していますが、1つのテーブルで実行できるはずだという卑劣な疑いがあります。

4

5 に答える 5

2

1つのクエリ内で問題を解決するために私が想像できる最もきちんとしたクエリは、次のクエリです。

select * from (
  select row_number() 
    over (partition by sender_id, receiver_id order by created_at desc) as rn, m.*
  from Messages m
  where (m.sender_id, m.receiver_id) in (
    select sender_id, receiver_id
    from Messages
    where sender_id = <id> or receiver_id = <id>
    group by sender_id, receiver_id
    order by max(created_at) desc
    limit 10 offset 0
  )
) res where res.rn <= 10

列には、各スレッド内の各メッセージのrow_number() over (partition by sender_id, receiver_id order by created_at desc)行番号が含まれます(1つのスレッドに対してのみクエリを実行するために個別のクエリを実行する場合は、レコード番号のようになります)。この行番号とは別に、メッセージが最上位の10個のスレッドに含まれているかどうかをクエリします(これはそれによって作成され(m.sender_id, m.receiver_id) in ...query...ます。最後に、最上位のメッセージを10個だけにしたい場合は、行番号を10以下に制限します。

于 2013-02-12T12:20:08.850 に答える
2

coulingの答えを取得し、それを少し変更して、共通のテーブル式を使用して2つのクエリを効果的に提供することをお勧めします。

WITH threads (sender_id, receiver_id, latest) as (
        select sender, 
               receiver,
               max(sent) 
          from sof_messages
         where receiver = <user>
            or sender = <user>
         group by sender,
               receiver
         order by 3
         limit 10
 ), 
 messages ([messages fields listed here], rank) as (
         select m.*, 
                rank() over (partition by (sender, receiver), order by sent desc)
           from sof_messages
          WHERE (sender, receiver) in (select (sender, receiver) from threads))
 SELECT * from messages where rank <= 10;

これには、プランナーがここでインデックスをいつ使用するかについてかなり良いアイデアを持てるようにするという利点があります。本質的に、クエリの3つの部分はそれぞれ独立して計画されています。

于 2013-02-15T09:37:39.650 に答える
1

何ができるかを示すためにこれを投稿しています。

私はそれを使用することを本当にお勧めしません。

2つの別々のクエリを実行する方がはるかに優れています。1つは最新の10個のスレッドを取得し、もう1つはスレッドごとに最新の10個のメッセージをプルバックするために繰り返します。

ただし、以下に示すrank() ウィンドウ関数を使用すると、目標を達成できます。

select * from (
      select message.*,
             rank() over (partition by message.sender, message.receiver 
                              order by sent desc )  
      from sof_messages message,
           (
            select sender, 
                   receiver,
                   max(sent) 
              from sof_messages
             where receiver = <user>
                or sender = <user>
             group by sender,
                   receiver
             order by 3
             limit 10
           ) thread
      where message.sender = thread.sender
        and message.receiver = thread.receiver
      ) message_list

where rank <= 10

ウィンドウ関数で目標を達成するいくつかの異なるクエリがありますが、どれも特にクリーンではありません。

于 2013-02-05T19:12:08.660 に答える
1

データが重複しているため、テーブルの作成Threadは間違っているように見えますが、ビューが役立つ場合があります。

CREATE VIEW threads AS 
  SELECT sender_id, receiver_id, min(created_at) AS t_date
  FROM messages
  GROUP BY sender_id,receiver_id;

スレッドの日付を、最も古いメッセージではなく、最新のメッセージの日付にする場合に変更min(created_at)します。max(created_at)

次に、次の方法でメッセージに結合し直すことができます。

SELECT ... FROM messages JOIN threads USING (sender_id,receiver_id)
于 2013-02-06T11:58:12.937 に答える
0

LIMIT 10私はこれをテストしていませんが、最近の10個のスレッドを提供するサブクエリを忘れたようです。

SELECT
  *
FROM
  (SELECT DISTINCT ON
     (sender_id, receiver_id) messages.* 
   FROM
     messages 
   WHERE
     (receiver_id = 5 OR sender_id = 5)
   ORDER BY
     sender_id, receiver_id, created_at DESC
   LIMIT
     10)   
  q
ORDER BY
  created_at DESC 
LIMIT
  10
OFFSET
  0;

(SQLをきれいに印刷したので、何が起こっているのかがわかりやすくなっています。)

于 2013-02-19T12:00:50.447 に答える