0

私はティンドラーに似た出会い系アプリを作っています。2 人のユーザーがお互いに好意を持っている場合、お互いにチャットできるはずです。チャットできる/既にチャットしているユーザーのリストのプルを処理する次のクエリを思いつきました-私が抱えている問題は、最新のチャットメッセージのみをプルしたいことです。チャット自体をクリックします。クエリは機能しますが、最新のチャット レコードではなく、最も古い (ID が最も小さい) チャット レコードが返されます。Order by は、正しい結果を返すことに影響を与えないようです。

$data = $this->db->select('users.id,display_name,city,state,gender,users_pictures.picture,users_chats.message')
                ->join('users_pictures','users_pictures.user_id=users.id')
                ->join('users_chats','users_chats.user_id=users.id OR users_chats.foreign_user_id=users.id','left outer')
                ->where('EXISTS (SELECT 1 FROM users_likes_dislikes ld WHERE (ld.foreign_user_id = '.$this->user_id.' AND ld.user_id=users.id AND ld.event_type=1) OR (SELECT 1 FROM users_likes_dislikes ld WHERE ld.foreign_user_id = users.id AND ld.user_id='.$this->user_id.' AND ld.event_type=1))', '', FALSE)
                ->where('NOT EXISTS (SELECT 1 FROM users_blocks ub WHERE (ub.foreign_user_id = users.id AND ub.user_id='.$this->user_id.') OR (SELECT 1 FROM users_blocks ub WHERE ub.foreign_user_id = '.$this->user_id.' AND ub.user_id=users.id))', '', FALSE)
                ->where('((users_chats.user_id='.$this->user_id.' OR users_chats.foreign_user_id='.$this->user_id.') OR (users_chats.user_id is NULL AND users_chats.foreign_user_id is NULL))')
                ->order_by('users_chats.id','DESC')
                ->group_by('users.id')              
                ->get('users')
                ->result_array();

users_chats の現在の mysql テーブルは次のとおりです。

id user_id foreign_user_id message created
1 1 4 test 2013-05-22 15:42:44
2 1 4 test2 2013-05-22 15:44:38

order_by によって、test2 メッセージが表示されていることが保証されると思いました。

出力例は次のとおりです。

Array ( [0] => Array ( [id] => 4 [display_name] => testinguser [city] => west hills [state] => ca [gender] => 2 [picture] => testasdfasdf.jpg [message] => test ) )

どんな助けでも大歓迎です:)

編集 - クエリ自体 (group by なしで、これは機能しますが、配列に同じユーザーの複数のエントリがないように、user.id にグループ化する必要があります):

SELECT
  `users`.`id`,
  `display_name`,
  `city`,
  `state`,
  `gender`,
  `users_pictures`.`picture`,
  `users_chats`.`message`
FROM (`users`)
  JOIN `users_pictures`
    ON `users_pictures`.`user_id` = `users`.`id`
  JOIN `users_chats`
    ON `users_chats`.`user_id` = `users`.`id`
       OR users_chats.foreign_user_id = users.id
WHERE EXISTS(SELECT
           1
         FROM users_likes_dislikes ld
         WHERE (ld.foreign_user_id = 1
            AND ld.user_id = users.id
            AND ld.event_type = 1)
          OR (SELECT
            1
              FROM users_likes_dislikes ld
              WHERE ld.foreign_user_id = users.id
              AND ld.user_id = 1
              AND ld.event_type = 1))
    AND NOT EXISTS(SELECT
             1
           FROM users_blocks ub
           WHERE (ub.foreign_user_id = users.id
              AND ub.user_id = 1)
            OR (SELECT
                  1
                FROM users_blocks ub
                WHERE ub.foreign_user_id = 1
                AND ub.user_id = users.id))
    AND ((users_chats.user_id = 1
       OR users_chats.foreign_user_id = 1)
      OR (users_chats.user_id is NULL
          AND users_chats.foreign_user_id is NULL))
ORDER BY `users_chats`.`created` DESC
4

3 に答える 3

1

あなたの group by 句が原因である可能性があります。グループ化操作が最初に行われ、最初の結果が得られると思います。これらすべての行を選択するのではなく (たくさんある場合は時間がかかります)、必要な数を指定する必要があります。必要な数を指定し、group by 句を削除します。日付列があるため、日付で並べ替える必要があります。それは役に立ちますか?

于 2013-05-22T20:09:15.043 に答える
1

データベースの抽象化でそれを行う方法がわかりませんが、必要なクエリは

SELECT
   `users`.`id`,
   `display_name`,
   `city`,
   `state`,
   `gender`,
   `users_pictures`.`picture`,
   chats1.`message`
  FROM (`users`)
    JOIN `users_pictures`
      ON `users_pictures`.`user_id` = `users`.`id`
    JOIN `users_chats` AS chats1
      ON (chats1.`user_id` = `users`.`id`
         OR chats1.foreign_user_id = users.id)

ここからが重要な部分です

         AND NOT EXISTS(
           SELECT *
             FROM users_chats AS chats2
             WHERE ((chats2.user_id = chats1.user_id AND chats2.foreign_user_id = chats1.foreign_user_id)
               OR (chats2.user_id = chats1.foreign_user_id AND chats1.user_id = chats2.foreign_user_id))
               AND chats2.created_date > chats1.created_date --which I assume is a time stamp
           )

それはきれいではありません、私は知っています。

  WHERE EXISTS(SELECT 1
    FROM users_likes_dislikes ld
      WHERE (ld.foreign_user_id = 1
        AND ld.user_id = users.id
        AND ld.event_type = 1)
        OR (SELECT 1
          FROM users_likes_dislikes ld
          WHERE ld.foreign_user_id = users.id
          AND ld.user_id = 1
          AND ld.event_type = 1)
    )
    AND NOT EXISTS(SELECT 1
       FROM users_blocks ub
       WHERE (ub.foreign_user_id = users.id
          AND ub.user_id = 1)
        OR (SELECT
              1
            FROM users_blocks ub
            WHERE ub.foreign_user_id = 1
            AND ub.user_id = users.id)
    )
    AND ((chats1.user_id = 1
        OR chats1.foreign_user_id = 1)
      OR (chats1.user_id is NULL
        AND chats1.foreign_user_id is NULL))
  ORDER BY `users_chats`.`created` DESC

基本的に、最近のメッセージがない場合にのみ正常に参加します。より優れたネイティブ ソリューションがいくつかあります。TSQL (Microsoft SQL Server) には CROSS APPLY があり、これはここで素晴らしいでしょうが、DB レイヤーについて詳しく知らなければ、確信が持てません。チャット構造の再設計を検討することをお勧めします:

Users(int id /*also other user info*/)
chats(int id, datetime date_initiated, bool /*or bit, or short int*/ is_active)
chat_users (int chat_id, int user_id)
chat_messages (int chat_id, int user_id /*author*/, datetime date_sent, varchar(n) message)

このような構造を使用すると、次のような最新のメッセージをすべて取得できます。

SELECT *
  FROM Users AS u
    INNER JOIN chat_users AS cu
      ON u.id = cu.user_id
    INNER JOIN chats AS c
      ON c.id = cu.chat_id
        AND c.is_active = 1
    INNER JOIN chat_messages AS m
      ON m.chat_id = c.id
        AND NOT EXISTS (
          SELECT 1
            FROM chat_messages AS m2
              WHERE m2.chat_id = m.chat_id
                AND m.date_sent < m2.date_sent
        )
     INNER JOIN Users as sender
       ON m.user_id = sender.id
  WHERE u.id = ###
  ORDER BY m.date_sent DESC

次のような「チャットの最新メッセージ」ビューを作成することもできます。

CREATE VIEW Chat_Recent AS
  SELECT * /* WHATEVER YOU LIKE */ 
    FROM chats AS c
      INNER JOIN chat_messages AS m
        ON m.chat_id = c.id
          AND NOT EXISTS (
            SELECT 1
              FROM chat_messages AS m2
                WHERE m2.chat_id = m.chat_id
                  AND m.date_sent < m2.date_sent
          )
       INNER JOIN Users as sender
         ON m.user_id = sender.id
于 2013-05-22T20:35:14.943 に答える