2

Facebookと非常によく似たメッセージシステムを実装しようとしています。メッセージテーブルは次のとおりです。

+--------+----------+--------+-----+----------+
| msg_id | msg_from | msg_to | msg | msg_time |
+--------+----------+--------+-----+----------+

ここmsg_fromに、msg_toにはユーザーIDがmsg_time含まれ、にはメッセージのタイムスタンプが含まれます。ユーザーのユーザーIDは、to列とfrom列の両方に表示され、別のユーザーには複数回表示されます。2人のユーザー間で最後に送信されたメッセージを選択するSQLクエリを作成するにはどうすればよいですか?(メッセージはどちらか一方から送信できます)1から2または2から1。

4

4 に答える 4

4

John Wooが方向性がないことを明らかにしたので、これが私の新しい答えです。

select *
from msgsList
where (least(msg_from, msg_to), greatest(msg_from, msg_to), msg_time)       
in 
(
    select 
       least(msg_from, msg_to) as x, greatest(msg_from, msg_to) as y, 
       max(msg_time) as msg_time
    from msgsList 
    group by x, y
);

出力:

| MSG_ID | MSG_FROM | MSG_TO |    MSG |                       MSG_TIME |
------------------------------------------------------------------------
|      1 |        1 |      2 |  hello | January, 23 2010 17:00:00-0800 |
|      5 |        1 |      3 | me too | January, 23 2012 00:15:00-0800 |
|      6 |        3 |      2 |  hello | January, 23 2012 01:12:12-0800 |

この入力の場合:

create table msgsList
(
  msg_id int,
  msg_from int, 
  msg_to int,
  msg varchar(10),
  msg_time datetime
);

insert into msgslist VALUES

(1, 1, 2, 'hello', '2010-01-23 17:00:00'),      -- shown
(2, 2, 1, 'world', '2010-01-23 16:00:00'),

(3, 3, 1, 'i am alive', '2011-01-23 00:00:00'),
(4, 3, 1, 'really', '2011-01-22 23:15:00'),
(5, 1, 3, 'me too', '2012-01-23 00:15:00'),     -- shown

(6, 3, 2, 'hello', '2012-01-23 01:12:12');      -- shown

SQLFiddleデモ


ANSI SQLがあなたのお茶である場合、これを行う方法は次のとおりです。http: //sqlfiddle.com/# !2/0a575/19

select *
from msgsList z
where exists
(
    select null
    from msgsList
    where 
      least(z.msg_from, z.msg_to) = least(msg_from, msg_to)
      and greatest(z.msg_from, z.msg_to) = greatest(msg_from, msg_to)
    group by least(msg_from, msg_to), greatest(msg_from, msg_to)
    having max(msg_time) = z.msg_time  
) ;
于 2012-08-19T15:46:52.377 に答える
2

こんなに簡単でしょうか?http://www.sqlfiddle.com/#!2/50f9f/1

set @User1 := 'John';
set @User2 := 'Paul';


select *
from
(
  select *
  from messages 
  where msg_from = @User1 and msg_to = @User2
  order by msg_time desc
  limit 1
) as x
union
select *
from
(
  select *
  from messages 
  where msg_from = @User2 and msg_to = @User1
  order by msg_time desc
  limit 1
) as x
order by msg_time desc

出力:

| MSG_ID | MSG_FROM | MSG_TO |         MSG |                      MSG_TIME |
----------------------------------------------------------------------------
|      2 |     Paul |   John | Hey Johnny! | August, 20 2012 00:00:00-0700 |
|      1 |     John |   Paul | Hey Paulie! | August, 19 2012 00:00:00-0700 |

MySQLのみがウィンドウ関数をサポートしている場合は、はるかに簡単になる可能性があります:http ://www.sqlfiddle.com/#!1/e4781/8

with recent_message as
(
select *, rank() over(partition by msg_from, msg_to order by msg_time desc) as r
from messages
)
select * 
from recent_message 
where r = 1 
    and 
    (
      (msg_from = 'John' and msg_to = 'Paul') 
      or
      (msg_from = 'Paul' and msg_to = 'John')
    )
order by msg_time desc;
于 2012-08-19T15:01:01.977 に答える
1

このような複雑なクエリには、TDQD —テスト駆動クエリ設計を使用します。あなたの経験とあなたが問題をどれだけよく理解しているかによって制御されるステップのサイズで、答えをステップバイステップで考案してください。

ステップ1—指定されたユーザー間の最新のメッセージの時刻を見つけます

この間、ユーザーIDは整数であると想定しています。1000と2000の値を使用しています。

SELECT MAX(msg_time) AS msg_time
  FROM message
 WHERE ((msg_to = 1000 AND msg_from = 2000) OR
        (msg_to = 2000 AND msg_from = 1000)
       )

ステップ2—最新のメッセージに対応するレコードを検索します

SELECT m.*
  FROM message AS m
  JOIN (SELECT MAX(msg_time) AS msg_time
          FROM message
         WHERE ((msg_to = 1000 AND msg_from = 2000) OR
                (msg_to = 2000 AND msg_from = 1000)
               )
       ) AS t
    ON t.msg_time = m.msg_time
 WHERE ((m.msg_to = 1000 AND m.msg_from = 2000) OR
        (m.msg_to = 2000 AND m.msg_from = 1000)
       )

同じ最新のタイムスタンプを持つこれらの文字の間に2つ(またはそれ以上)のメッセージがある場合、それらはすべて選択されます。現在、衝突を選択する根拠はありません。それが問題だと思われる場合は、MAX(msg_id)上記のクエリを使用して(サブクエリとして)検索するように手配できます。

SELECT m2.*
  FROM message AS m2
  JOIN (SELECT MAX(m.msg_id) AS msg_id
          FROM message AS m
          JOIN (SELECT MAX(msg_time) AS msg_time
                  FROM message
                 WHERE ((msg_to = 1000 AND msg_from = 2000) OR
                        (msg_to = 2000 AND msg_from = 1000)
                       )
               ) AS t
            ON t.msg_time = m.msg_time
         WHERE ((m.msg_to = 1000 AND m.msg_from = 2000) OR
                (m.msg_to = 2000 AND m.msg_from = 1000)
               )
       ) AS i
    ON i.msg_id = m2.msg_id

警告コードはどのDBMSでも正式にテストされていません。

于 2012-08-19T15:47:53.500 に答える
0

それを考えた後、私はこれを思いついた:

SELECT min_user AS min(msg_from, msg_to), max_user AS max(msg_from, msg_to), 
max(msg_date) FROM msg GROUP BY min_user, max_user

メッセージから追加データを取得する方法はまだよくわかりませんが、少し考えてみます。

于 2012-08-19T14:16:39.620 に答える