Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
288 views
in Technique[技术] by (71.8m points)

mysql - SQL: How to merge two complex queries into one, where the second one needs data from the first one

The goal is to load a list of chats where the user sending the request is a member in. Some of the chats are group chats (more than two members) and there I want to show the profile pictures from the users who wrote the last three messages.

The first query to load meta data like the title and the timestamp of the chat is:

SELECT Chat_Users.ID_Chat, Chats.title, Chats.lastMessageAt
FROM Chat_Users
    JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) = 2 
AND MAX(Chat_Users.ID_User = $userID) > 0
ORDER BY Chats.lastMessageAt DESC
LIMIT 20

The query to load the last three profile pictures from one of the chats loaded with the query above is:

SELECT GROUP_CONCAT(innerTable.profilePictures SEPARATOR ', ') AS 'ppUrls',     
        innerTable.ID_Chat 
FROM 
    (
    SELECT Chat_Users.ID_Chat, Users.profilePictureUrl AS profilePictures
    FROM Users
        JOIN Chat_Users ON Chat_Users.ID_User = Users.ID
        JOIN Chat_Messages ON Chat_Messages.ID_Chat = Chat_Users.ID_Chat
    WHERE Chat_Users.ID_Chat = $chatID
    ORDER BY Chat_Messages.timestamp DESC
    LIMIT 3
    ) innerTable
GROUP BY innerTable.ID_Chat

Both are working separately but I want to merge them together so I don't have to run the second query in a loop due to performance reasons. Unfortunately I have no idea how this can be achieved because the second query needs the $chatID, which it only gets from the first query.

So to clarify the desired result: The list with the profile picture urls (second query) should be just another column in the result of the first query.

I hope it is explained in a reasonably understandable way. Any help would be much appreciated.

Edit: Sample data from the affected tables:

Table "Chats": enter image description here

Table "Chat_Users":

enter image description here

Table "Chat_Messages":

enter image description here

Table "Users":

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This fufils the brief, however it requires a view because MySQL 5.x doesn't support the WITH clause.

It's long and cluncky and I've tried to shorten it but this is as good as I can get, hopefully someone will pop up in the comments with a way to make it shorter!

The view:

CREATE VIEW last_interaction AS
SELECT
    id_chat,
    id_user,
    MAX(timestamp) AS timestamp
FROM chat_messages
GROUP BY id_user, id_chat

The query:

SELECT
    Chat_Users.ID_Chat,
    Chats.title,
    Chats.lastMessageAt,
    urls.pps AS profilePictureUrls
FROM Chat_Users
    JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
    JOIN (
        SELECT
            lo.id_chat,
            GROUP_CONCAT(users.profilePictureUrl) AS pps
        FROM last_interaction lo
        JOIN users ON users.id = lo.id_user
        WHERE (
            SELECT COUNT(*) -- the amount of more recent interactions
            FROM last_interaction li
            WHERE (li.timestamp = lo.timestamp AND li.id_user > lo.id_user)
        ) < 3
        GROUP BY id_chat
    ) urls ON urls.id_chat = Chats.id
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) > 2 
AND MAX(Chat_Users.ID_User = $userID)
ORDER BY Chats.lastMessageAt DESC
LIMIT 20

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...