i have troubles finding the right sql-statement
I have 3 tables:
1.) Messages
2.) Mail_Inbox
3.) Mail_Outbox
New Messages are stored in Messages and its ID is stored in both other tables.
User can delete their mails from inbox and outbox folder.
I want du parse a single sql-statement to delete all entries from Messages where no entries in Inbox AND Outbox with the corresponding ID exsits.
I still found a working statement, which tells me the right IDs, but i can't find a way to delete them.
SELECT Messages.Message_ID
FROM `Messages`
LEFT JOIN Mail_Inbox On Messages.Message_ID = Mail_Inbox.Message_ID
LEFT JOIN Mail_Outbox On Messages.Message_ID = Mail_Outbox.Message_ID
WHERE ISNULL(Mail_Outbox.Mail_ID) AND ISNULL(Mail_Outbox.Mail_ID)
I tried this:
DELETE FROM Messages
WHERE Message_ID = (SELECT Messages.Message_ID
FROM `Messages`
LEFT JOIN Mail_Inbox On Messages.Message_ID = Mail_Inbox.Message_ID
LEFT JOIN Mail_Outbox On Messages.Message_ID = Mail_Outbox.Message_ID
WHERE ISNULL(Mail_Outbox.Mail_ID) AND ISNULL(Mail_Outbox.Mail_ID))
But got this error: You can't specify target table 'Messages' for update in FROM clause
:-(
It is not possible to use a table you want to delete from inside s subquery as per DELETE-documentation
SELECT
Messages.Message_ID
FROM `Messages`
LEFT JOIN Mail_Inbox ON Messages.Message_ID = Mail_Inbox.Message_ID
LEFT JOIN Mail_Outbox ON Messages.Message_ID = Mail_Outbox.Message_ID
WHERE ISNULL(Mail_Inbox.Mail_ID) AND ISNULL(Mail_Outbox.Mail_ID)
becomes
DELETE
Messages
FROM `Messages`
LEFT JOIN Mail_Inbox ON Messages.Message_ID = Mail_Inbox.Message_ID
LEFT JOIN Mail_Outbox ON Messages.Message_ID = Mail_Outbox.Message_ID
WHERE ISNULL(Mail_Inbox.Mail_ID) AND ISNULL(Mail_Outbox.Mail_ID)
DELETE FROM Messages WHERE Message_ID IN (some subquery that select Message_IDs)
Your current subquery will give you all Message_IDs and will delete all messages because you use a LEFT JOIN. Use INNER JOIN.
Related
I have a table where my private message are stored which I done with my friends.
I have table structure as shown in image
Suppose my logged user_id is 1, Now I want show his last five unique conversations with last message of each conversation.
Not very elegant but working:
SELECT m1.*
FROM private_msgs m1 LEFT OUTER JOIN private_msgs m2
ON (m1.message_from_uid=m2.message_from_uid AND m1.message_to_uid=m2.message_to_uid AND m1.message_time<m2.message_time)
WHERE m2.message_id IS NULL AND m1.message_from_uid=$uid
ORDER BY message_time DESC;
Add limit to 5 or whatever you want
EDIT. Sorry I didn't notice that UID can be as sender or receiver. Can you publish table with sample data for example here: http://sqlfiddle.com?
Anyway that should to the trick:
WHERE m2.message_id IS NULL AND (m1.message_from_uid=$uid OR m1.message_to_uid=$uid)
Here it is, long and ugly. You can replace temporary tables as subqueries in one big final query. Explanations are at the end.
CREATE TEMPORARY TABLE IF NOT EXISTS t1 AS
(SELECT message_id, message_from_uid, message_to_uid, message_time
FROM private_msgs
WHERE message_from_uid=$UID)
UNION
(SELECT message_id, message_to_uid, message_from_uid, message_time
FROM private_msgs
WHERE message_to_uid=$UID);
CREATE TEMPORARY TABLE IF NOT EXISTS t2 AS SELECT * FROM t1;
SELECT m.*
FROM (t1 LEFT OUTER JOIN t2
ON (t1.message_from_uid=t2.message_from_uid AND t1.message_to_uid=t2.message_to_uid AND t1.message_time<t2.message_time))
JOIN private_msgs m ON t1.message_id=m.message_id
WHERE t2.message_id IS NULL AND t1.message_from_uid=$UID
ORDER BY t1.message_time DESC;
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
t1 temporary table takes all messages where $UID is a sender and union them with messages where $UID is receiver, but puts $UID as sender. In t1 doesn't matter who is sender/receiver. What does matter that these are conversations between $UID and someone else.
t2 is copy of t1, because in MySQL you can't join temporary table with itself.
And finally we do the query. We join t1 and t2, take only newest messages in every conversation and sort it from latest to oldest. As in t1 and t2 sender and receiver are mixed we do the last join with private_msg to get original data.
PS. Sorry for my english, I'm not native speaker.
Found among my old code:
select m.*
from private_messages m
join user_table s ON m.sender_id = s.id
join user_table r ON m.receiver_id = r.id
join (select if(sender_id = $UID, receiver_id, sender_id) as user_id_other,
max(posted_at) AS date_time_max from private_messages
where (sender_id = $UID OR receiver_id = $UID)
group by if(sender_id=$UID, receiver_id, sender_id)
) AS t
on if(m.sender_id = $UID, m.receiver_id, m.sender_id) = user_id_other
AND m.posted_at=date_time_max
where m.sender_id = $UID OR m.receiver_id = $UID
order by m.posted_at desc
Not responsible for result, but this can help. By itself variables must be replaced, I did not modify the code very much, as you can see. But this piece of... code... worked.
In your case smths like:
select m.*
from YOUR_MESSAGES_TABLE m
--- OPTIONAL: join YOUR_USER_TABLE s ON m.message_from_uid = s.id
--- OPTIONAL: join YOUR_USER_TABLE r ON m.message_to_uid = r.id
join (select if(message_from_uid = $YOUR_USER_ID, message_to_uid, message_from_uid) as user_id_other,
max(message_time) AS date_time_max from YOUR_MESSAGES_TABLE
where (message_from_uid = $YOUR_USER_ID OR message_to_uid = $YOUR_USER_ID)
group by if(message_from_uid=$YOUR_USER_ID, message_to_uid, message_from_uid)
) AS t
on if(m.message_from_uid = $UID, m.message_to_uid, m.message_from_uid) = user_id_other
AND m.message_time=date_time_max
where m.message_from_uid = $UID OR m.message_to_uid = $UID
order by m.message_time desc limit 5
Test: http://sqlfiddle.com/#!9/f66fd8/4
I have fours tables and I wanted to join all three tables with the one table.
I have listed my problem as follows:
Tables:
users
user_training
user_courses
user_certificates
I wanted to get the data from [2,3,4] tables that user_id field matches with the users table ID field.
When I try the INNER JOIN it gives me the result for users that are common in all the tables, But I just wanted to check the [2,3,4] tables with the table [1] Records.
My Query...
SELECT A.training_name AS 'training_name', C.course_name AS 'course_name', D.certificate_name AS 'certificate_name'
FROM user_training AS A INNER JOIN users AS B ON A.user_id=B.ID INNER JOIN user_courses AS C ON B.ID = C.user_id INNER JOIN user_certificates AS D ON B.ID = D.user_id;
Thanks in Advance.
use left join
select u.* from users u
left join user_training ut on ut.user_id=u.user_id
left join user_courses uc on uc.user_id=u.user_id
left join user_certificates uct on uct.user_id=u.user_id
With this one you are getting all users and their respective trainings:
SELECT *
FROM `users`
LEFT JOIN `user_training` ON `users`.`id` = `user_training`.`user_id`
Changing *_trainig to *_courses or *_certificates will return all users with respected courses or certificates.
If you need to get data in one query, try this one:
SELECT *
FROM `users`
LEFT JOIN `user_training` ON `users`.`id` = `user_training`.`user_id`
LEFT JOIN `user_courses` ON `users`.`id` = `user_courses`.`user_id`
LEFT JOIN `user_certificates` ON `users`.`id` = `user_certificates`.`user_id`
If user has no trainings, courses, certificates all remaining fields will be null-ed.
I have a query that looks up people's full names based on their record ID's in a table called users. The full names are tied to their roles in another table (table1). This requires multiple joins to the users table:
SELECT table1.id, users.full_name AS "Requester",
users.full_name AS "Approver,"
users.full_name AS "Ordered By",
users.full_name AS "Received By"
FROM table1
JOIN users AS users
ON table1.requester_id = users.id
JOIN users AS users2
ON table1.approver_id = users2.id
JOIN users AS users3
ON table1.ordered_by = users3.id
JOIN users AS users4
ON table1.received_by = users4.id
WHERE table1.deleted_record !=1;
The problem I'm having is with ordered_by and received_by. Often, they don't yet exist, because the order has neither been ordered nor received, so the ID for each can be 0, which has no corresponding value in the userstable. When I run this query, I should get back all 475 records that exist, but I only get back 365, because of those 0 values. How can I modify this query to make sure all rows are returned, even if ordered_by and/or received_by = 0?
First, your primary table driving the query should be table1. Then, you are using JOIN instead of LEFT JOIN. LEFT JOIN will give you a null result if no link, but not fail. In which case, you might have to use an IF for your fields value
SELECT table1.id, req.full_name AS "Requester",
app.full_name AS "Approver",
ordr.full_name AS "Ordered By",
rec.full_name AS "Received By"
FROM table1
LEFT JOIN users AS req
ON table1.requester_id = req.id
LEFT JOIN users AS app
ON table1.approver_id = app.id
LEFT JOIN users AS ordr
ON table1.ordered_by = ordr.id
LEFT JOIN users AS rec
ON table1.received_by = rec.id
WHERE table1.deleted_record !=1;
This should do it
You are looking for left join:
SELECT t1.id, ur.full_name AS "Requester",
ua.full_name AS "Approver,"
uo.full_name AS "Ordered By",
urv.uo AS "Received By"
FROM table1 t1 LEFT JOIN
users ur
ON t1.requester_id = ur.id LEFT JOIN
users ua
ON t1.approver_id = ua.id LEFT JOIN
users uo
ON t1.ordered_by = uo.id LEFT JOIN
users urv
ON t1.received_by = urv.id
WHERE t1.deleted_record <> 1;
Note that I changed the aliases on the users references from fairly meaningless u1, u2, etc. to ua, uo, and so on. Also, these need to be used in the SELECT to get the right full name.
Please tell me what is wrong with this code, this returns all the conversations, but it returns latest message for only first conversation.
SELECT
conversations.*,
messages.message,
patients.first_name as fullname,
patients.city,
patients.thumb,
patients.gender,
patients.online_status
FROM
patients
INNER JOIN conversations
ON conversations.patient_id_fk = patients.id
LEFT JOIN messages
ON messages.conversation_id = conversations.id
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.therapist_id_fk = conversations.therapist_id_fk
)
WHERE conversations.therapist_id_fk='1'
GROUP BY conversations.id
ORDER BY messages.message_id DESC
You want the maximum message for the conversation not the therapist, so I suspect you want this in this subquery:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.message_id = conversations.id
)
I'm not sure the outer GROUP BY is correct (it may not be needed at all and it seems to conflict with the columns of the SELECT), but without sample data and desired results, it is hard to tell.
EDIT:
I see, the problem is the filtering on therapist id in the outer query. That makes this a bit more complicated:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z JOIN
conversations c
ON m.conversation_id = c.id
WHERE c.therapist_id_fk = 1 AND -- restriction on subquery
z.message_id = conversations.id -- correlation to outer query
)
Don't use single quotes for constants unless the column is a string or date.
SELECT
conversations.*,
messages.message,
patients.first_name as fullname,
patients.city,
patients.thumb,
patients.gender,
patients.online_status
FROM
patients
INNER JOIN conversations
ON conversations.patient_id_fk = patients.id
LEFT JOIN messages
ON messages.conversation_id = conversations.id
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.conversation_id = conversations.id
)
WHERE conversations.therapist_id_fk='1'
ORDER BY messages.message_id DESC
Found the issue. It was here:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.conversation_id = conversations.id
)
I was using this:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.therapist_id_fk = conversations.therapist_id_fk
)
I was able to solve the problem with this query
SELECT conversations.*,m.message FROM `conversations` LEFT JOIN messages as m on conversations.id = m.conversation_id AND m.id = (SELECT MAX(ms.id) from messages as ms where ms.conversation_id = conversations.id)
I left join the messages and when doing so i select the top most message. Order by can be used to give the order by date
Your problem is the bit:
SELECT MAX(message_id)
FROM messages z
WHERE z.therapist_id_fk = conversations.therapist_id_fk
This query only matches exactly one message. You'd need something a bit different.
Try this:
SELECT
conversations.*,
messages.message,
patients.first_name as fullname,
patients.city,
patients.thumb,
patients.gender,
patients.online_status
FROM
patients
INNER JOIN conversations
ON conversations.patient_id_fk = patients.id
LEFT JOIN messages
ON messages.conversation_id = conversations.id
WHERE conversations.therapist_id_fk='1'
AND messages.message_id IN ( -- new bit
SELECT MAX(message_id) FROM messages z GROUP BY z.conversation_id
)
GROUP BY conversations.id
ORDER BY messages.message_id DESC
I've build the following query:
(SELECT
privatemsgs.id,
privatemsgs.useradn,
privatemsgs.useraid,
privatemsgs.title,
privatemsgs.created,
privatemsgs.timee,
privatemsgs.isread,
u.photo AS creatorphoto,
privatemsgs.relatedto
FROM privatemsgs
LEFT JOIN
users AS u ON(privatemsgs.useraid = u.id)
WHERE userbid='5'
AND relatedto=0 and bdel=1)
UNION ALL
(SELECT
privatemsgs.id,
privatemsgs.useradn,
privatemsgs.useraid,
privatemsgs.title,
privatemsgs.created,
privatemsgs.timee,
privatemsgs.isread,
u.photo AS creatorphoto,
rel.relatedto
FROM privatemsgs AS rel
JOIN privatemsgs ON(rel.relatedto = privatemsgs.id)
LEFT JOIN
users AS u ON(rel.useraid = u.id)
WHERE rel.userbid='5')
GROUP BY id
ORDER BY timee DESC
This query select all Privatemsgs from the tables, and acting like mail,FOR EX:
If I sent a msg to user b, and user b answered me. I want to display the msg in inbox and outbox of each user.
A comment to private msg marked as "relatedto" the id of the main msg.
The query works, but duplicate the msgs in display (same msg display many times)
I tried to do "GROUP BY id" in order to fix it but i got the error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'GROUP BY id ORDER BY timee DESC'
THANK YOU!!
First, as per PM 77's comment, a union instead of a union all will solve your problem of duplicates. You don't need a group by clause at all.
Second, you might have a logic error. The top have of your union query has this:
FROM privatemsgs
LEFT JOIN
users AS u ON(privatemsgs.useraid = u.id)
WHERE userbid='5'
AND relatedto=0 and bdel=1)
If any of those fields in the where clause are in the users table, your left join has become an inner join. To keep it as a left join, you have to put all the filters in the join, like this:
FROM privatemsgs
LEFT JOIN
users AS u ON privatemsgs.useraid = u.id
AND userbid='5'
AND relatedto=0 and bdel=1)