MYSQL: Help Querying Results From Three Tables - mysql

I am wanting to query results from two "content" tables and one "users" table.
The two primary content files have identical field names, yet different fields - as one of the tables is for comments made by registered members and the other for comments by guests.
Consider the "widgetID" to be the primary IDentification of the widget for which I am trying to loop through the comments. Likewise the "active" is a 0 or 1 for whether it has been approved to be viewable.
table_widget:
id
datetime
usersID
message
active
table_member_comments:
id
datetime
widgetID
usersID
message
active
table_guest_comments:
id
datetime
widgetID
usersName
UsersEmail
message
active
table_users:
id
datetime
usersID
fullName
active
So what I have been trying to figure out is how to query both tables together to end up with one result with which to loop and display both the members and public comments.
I am pretty sure I need to do a JOIN, and I have tried and failed to wrap my head around the correct procedure.
I need to do the initial SELECT ... with the JOIN in here that I am not getting... WHERE widgetID = ? AND u.active = '1' ORDER BY DateTime DESC LIMIT 0, 100
Its the darn part in the middle that is tweaking me up here. Would love to have somebody show me how to do this. Thanks.

A join isn't the best option here, you need to use UNION which will combine data not join it together.
For example:
SELECT mydata.message FROM
(
SELECT datetime, message FROM table_member_comments WHERE widgetID = 100 AND active = 1
UNTION ALL
SELECT datetime, message FROM table_guest_comments WHERE widgetID = 100 active = 1
) mydata
ORDER BY mydata.datetime ASC

Use UNION to tack on the results from another query onto the same result set:
SELECT a.*
FROM (
SELECT c.message, c.datetime, u.fullName
FROM table_member_comments c
JOIN table_users u ON c.usersID = u.usersID
WHERE c.widgetID = ? AND c.active = 1
UNION ALL
SELECT message, datetime, usersName
FROM table_guest_comments
WHERE widgetID = ? AND active = 1
) a
ORDER BY a.datetime DESC
LIMIT 0,100

Related

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":
Table "Chat_Users":
Table "Chat_Messages":
Table "Users":
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

Return 0 if no records found on COUNT(*) query In MYSQL

I want to get '0' when no records found on execution of sql query. I try so many things like ifnull(count(*),0) but not gives me the result which i want.
Query is mention as below :
SELECT chat_room_id,COUNT(*) FROM chat WHERE sender_id=13 GROUP BY chat_room_id
It gives me the result as below:
__________________________
| chat_room_id | Count(*) |
--------------------------
It not return any 'null' or '0' , so i want to get '0' OR 'null' if no records found.
As long as there is a table Chats with field sender_id then the following will return a row even if the sender_Id 13 does not exist in the table.
set #sender = 13;
set #chats = (select count(*) from Chat where sender_id = #sender);
select #sender AS sender_id, IFNULL(#chats,0) AS Count_chats
;
See this working at SQLFiddle
AFTER YOUR EDIT CHANGES
Try this:
SELECT distinct c1.chat_room_id,
(select count(*) from chat c2 c2.chat_room_id = c1.chat_room_id and c2.sender_id = 13)
FROM chat c1
Pay attention:
A table named CHAT I suppose contains all chats in your DB and no the messages and senders.
Instead if you have a table named CHAT_ROOM with the list of all chats so your query becomes:
SELECT chat_room_id,
(SELECT COUNT(*)
FROM chat
WHERE chat.chat_room_id = chat_room.chat_room_id AND chat.sender_id = 13)
FROM chat_room
You can not query data which is not in the source tables or which is not available with calculation.
If you have a table which contains all available chat rooms, you can use that table to get the desired record with a simple query using LEFT OUTER JOIN
SELECT
CR.chat_room_id,
COUNT(C.chat_room_id)
FROM
chat_rooms CR
LEFT JOIN chat C
ON CR.chat_room_id = C.chat_room_id
AND C.sender_id = 13 -- Moved from WHERE to keep all non-matching records from CR
GROUP BY
CR.chat_room_id
If you do not have a specific table, you can use LEFT OUTER JOIN and a subquery to get the result:
SELECT
CR.chat_room_id,
COUNT(C.chat_room_id)
FROM
(SELECT DISTINCT chat_room_id FROM chat) CR
LEFT JOIN chat C
ON CR.chat_room_id = C.chat_room_id
AND C.sender_id = 13 -- Moved from WHERE to keep all non-matching records from CR
GROUP BY
CR.chat_room_id
SQLFiddle demo for the second query
Are you looking to return all the chat_room_ids in case the sender id does not exist?
like, if say sender_id 9 does not exist, what are you looking to return?
In case you wanted something like
chat_room_id count
NO_RECORDS 0
then the below query will work:
declare #Chat_Room_Id varchar(3),
#Count int
select #Chat_Room_Id = cast(chat_room_id as varchar(3)), #Count = count(*) from chat where sender_id=13 group by chat_room_id
select coalesce(#Chat_Room_Id, 'NO RECORDS') chat_room_id, coalesce(#Count, 0) count
In case I did not understand, can you please clarify further?
-hth
Sourav
you can use mysql_affected_rows(); method to get the details of rows affected with your query.
check this
and this
You can put the sql in a sub query this will always return a row
select sub.id,sub.counter from (select sender_id as id,count(*) as counter from chat where sender_id = 13) sub
This will return 0 if there are no records or 1 if there are.

sql distinct function trouble

I have a query
SELECT m1.mid mid, m1.uid uid, m1.date, m1.body body
FROM messages m1
WHERE m1.chat_id IS NULL
and deleted = 0
AND m1.date in
(
SELECT MAX(m2.date)
FROM messages m2
WHERE m2.uid = m1.uid
AND m2.chat_id IS NULL
and m2.deleted = 0
)
This query was not actually written by me, I got help here - hanks them a lot.
But I have a problem. When there is more than one message with the same date and uid(from the same user) values, i get two or more records with the same date and uid, but i'd like to have only one( no matter which, or the message with the greatest mid f.e.)
I've tried to use distinct(uid) with no success result. Is there any other way to achive the result i want?
You can use this solution to find the most recent message per uid:
SELECT b.*
FROM
(
SELECT MAX(mid) AS maxmid
FROM messages
WHERE chat_id IS NULL
GROUP BY uid
) a
INNER JOIN messages b ON a.maxmid = b.mid
Rather you could add an extra column "count" and display the number of messages from a user on the same day!!
By this you can avoid, getting duplicate records!

Creating a leaderboard (ranked list of members) in MySQL with nested queries

I'm trying to create a leaderboard of members on my site by using the two nested queries below.
The first query grabs a list of members:
SELECT member_id, username, screen_name FROM exp_members WHERE group_id IN (1,5) LIMIT 100
The second query is nested inside the first query, and grabs a count of how many times a member's entries have been favorited:
SELECT COUNT(*) AS favorite_count
FROM qb_channel_titles, qb_channel_data, qb_matrix_data
WHERE qb_channel_titles.channel_id = '1'
AND qb_channel_titles.entry_id = qb_channel_data.entry_id
AND qb_channel_titles.entry_id = qb_matrix_data.entry_id
AND field_id = '13'
AND author_id = 'MEMBER_ID_FROM_FIRST_QUERY'
ORDER BY favorite_count DESC"
}
So the code I have is like:
[first query]
[second query]
..output one row of the leaderboard..
[/second query]
[/first query]
Nesting the second query inside the first gives me the proper list of members and the number of votes they've each received, but the list is sorted on the first (outer) query rather than the second (inner) query.
Two questions:
How do I sort the list of members by favorite_count in descending order?
What is the most resource efficient way to do this? I suspect that nesting queries isn't the best way to go, but I honestly don't know any better.
Are you trying to do something like this?
SELECT
member_id,
username,
screen_name,
(SELECT COUNT(*) AS favorite_count
FROM qb_channel_titles, qb_channel_data, qb_matrix_data
WHERE qb_channel_titles.channel_id = '1'
AND qb_channel_titles.entry_id = qb_channel_data.entry_id
AND qb_channel_titles.entry_id = qb_matrix_data.entry_id
AND field_id = '13'
AND author_id = member_id
ORDER BY favorite_count DESC") as "Votes"
FROM
exp_members
WHERE
group_id IN (1,5)
ORDER BY
(SELECT COUNT(*) AS favorite_count
FROM qb_channel_titles, qb_channel_data, qb_matrix_data
WHERE qb_channel_titles.channel_id = '1'
AND qb_channel_titles.entry_id = qb_channel_data.entry_id
AND qb_channel_titles.entry_id = qb_matrix_data.entry_id
AND field_id = '13'
AND author_id = member_id
ORDER BY favorite_count DESC")
LIMIT 100
You could also put this query in a view and query the view (sort of nesting another query performance-wise). I'm not expert on performance, but I'd say you could use a trigger and keep the favorite_count in another table, and every time an user favorites something the trigger will update. The other table could have just ID|COUNT. This will increase time when favoriting but reduce time to check the leaderboard, so the efficiency will depend on your user profile for favoriting or viewing the leaderboards...

Select value that is not "*username*"

My problem is this:
I need to select values from "groups" table that do not contain specific "user_id".
So I execute this:
SELECT DISTINCT group.active, hide_group.user_id, group.group_id, hide_group.hide, group.desc AS group_desc
FROM group
LEFT JOIN hide_group ON hide_group.group_id = group.group_id
WHERE (
hide_group.user_id != 'testuser'
OR hide_group.user_id IS NULL
AND (
hide != 'true'
OR hide IS NULL
)
)
AND active =1
ORDER BY `group`.`group_id` ASC
LIMIT 0 , 500
But now lets say we have such records in my 'group_hide' table:
[group_hide]
(user_id, group_id, hide)
john, ABC, true;
joe, ZZZ, true;
mark, ABC, true;
So now when I do my query, mark still sees ABC group, because the condition is true and valid because user_id = john and we therefor take the ABC value, even when it is hidden for mark.
I have tried changing this query several times, but I can't figure out this simple problem.
I suggest using a subquery:
SELECT *
FROM group
WHERE group_id NOT IN (
SELECT group_id
FROM group_hide
WHERE user_id = 'testuser'
AND hide = true)
AND active =1
ORDER BY `group`.`group_id` ASC
LIMIT 0 , 500
You can use Having to apply conditions on the JOIN results. I would also use GROUP BY rather than DISTINCT.
I am not sure I completely follow your WHERE condition (I think you have some mixup with the parenthesis), but here's a version which may get you the result you need:
SELECT DISTINCT group.active, hide_group.user_id, group.group_id, hide_group.hide, group.desc AS group_desc
FROM group
LEFT JOIN hide_group ON hide_group.group_id = group.group_id
WHERE active =1
HAVING (hide_group.user_id != 'testuser'
OR hide_group.user_id IS NULL)
AND
(hide != 'true'
OR hide IS NULL)
ORDER BY `group`.`group_id` ASC
LIMIT 0 , 500
If you post the tables structure, some sample data and a sample wanted output, you'll get better responses with working queries...