I have two type of message, one is private message and another is credit message are inserting into two different table. Now I'm trying to fetch the data.
SELECT * ,(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'like') AS likes,
(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'dislike') AS dislikes
FROM messages m
WHERE 1 #and hidden is null
and recipient_id = 1
ORDER BY datetime DESC
and
SELECT * ,(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'like') AS likes,
(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'dislike') AS dislikes
FROM private_messages m
WHERE 1 #and hidden is null
and recipient_id = 1
ORDER BY datetime DESC
Now want to merge them into one query one extra parameter will show its private message or credit message.
If the existing queries do what you want/need, UNION will make it pretty simple to combine them, something like;
SELECT * FROM (
SELECT is_private 0, <field1>,<field2>,<field3>, ... ,(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'like') AS likes,
(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'dislike') AS dislikes
FROM messages m
WHERE 1 #and hidden is null
and recipient_id = 1
UNION ALL
SELECT 1, <field1>, <field2>, <field3>, ... ,(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'like') AS likes,
(SELECT COUNT(*)
FROM votes
WHERE message_id = m.message_id
AND vote_type = 'dislike') AS dislikes
FROM private_messages m
WHERE 1 #and hidden is null
and recipient_id = 1
)
ORDER BY datetime DESC
Note that you need to select the same number/order of columns from both queries for the union to work. SELECT * makes it hard to verify if/that that is the case. If
You can use UNION to combine mysql query:
SELECT id FROM table_user
UNION
SELECT COUNT(vote) FROM table_vote
You can use multiple UNION query by UNION.
Like,
Query 1
UNION
Query 2
UNION
.....
.....
.....
Query n
I think the following is a much simpler way to express the logic:
select m.*, v.likes, v.dislikes
from (select v.message_id, sum(vote_type = 'like') AS likes, sum(vote_type = 'dislike') AS dislikes
from votes v
group by v.message_id
) v join
(select m.*, 'Message' as which
from messages
union all
select pm.*, 'PrivateMessage' as which
from private_messages pm
) m
on v.message_id = m.message_id;
Related
select mt.from_user, mt.to_user, mt.group_id, g.name, g.created_by as adminuser,
msg.*,
(
SELECT id
from messages
where t.thread_id = thread_id
and id NOT IN (
SELECT message_id from message_deleted
where user_id=275 and status='deleted' )
order by CreatedDate DESC
limit 1
) as msgid,
(
SELECT CreatedDate
from messages
where t.thread_id = thread_id
and id NOT IN (
SELECT message_id from message_deleted
where user_id=275 and status='deleted' )
order by CreatedDate DESC
limit 1
) as msgDate
from user_thread as t
left join message_thread as mt ON t.thread_id = mt.id
left join group_master as g ON mt.group_id = g.id
left join group_member as gm ON gm.group_id = g.id
left join messages as msg ON t.thread_id = msg.thread_id
where ( gm.user_id=275
or msg.from_id=275
or msg.to_id=275
)
and t.status = 'Active'
group by mt.id
order by msgDate DESC
This takes about 50 sec.
In above code, I have try to split above query and note that below subquery take too much time to execute. Can I convert subquery into join. please help me. I am stuck.please note that all tables which are joined are necessary.
(
SELECT id
from messages
where t.thread_id = thread_id
and id NOT IN (
SELECT message_id from message_deleted
where user_id=275 and status='deleted' )
order by CreatedDate DESC
limit 1
) as msgid,
(
SELECT CreatedDate
from messages
where t.thread_id = thread_id
and id NOT IN (
SELECT message_id from message_deleted
where user_id=275 and status='deleted' )
order by CreatedDate DESC
limit 1
) as msgDate
First, You are misusing a notorious MySQL extension to GROUP BY. This will probably cause your results to be unpredictable. Read this. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
Second, You have a couple of nested dependent subqueries. The first of them is this.
(select id
from messages
where t.thread_id = thread_id
and id NOT IN (select message_id
from message_deleted
where user_id=275
and status='deleted')
order by CreatedDate DESC limit 1) as msgid
Such nested dependent subqueries perform notoriously badly. They're even worse when they contain LIMIT clauses. Your route to fixing this is refactoring into an independent query and then JOINing it.
This may work as a replacement for the query to find the most recent undeleted message on the thread.
SELECT MAX(m.id) id, m.thread_id
FROM messages m
LEFT JOIN message_deleted d
ON m.id = d.id
AND d.user_id = 275
AND d.status = 'deleted'
WHERE d.id IS NULL
GROUP BY m.thread_id
This uses the LEFT JOIN .... IS NULL pattern in place of NOT IN. It's faster. It uses the MAX(id) method of finding the most recent row in a table in place of the ORDER BY CreatedDate DESC LIMIT 1 method, which is also much faster. It's good because it's guaranteed to generate either 0 or 1 row per value of thread_id. That means you can use it in a LEFT JOIN ... ON ... thread_id operation and not add any rows to your result set.
You can test this subquery by running it. Then you JOIN it, as if it were a table, to the rest of your query, something like this.
SELECT whatever,
q.id, r.CreatedDate
FROM whatever
LEFT JOIN (
SELECT MAX(m.id) id, m.thread_id
FROM messages m
LEFT JOIN message_deleted d
ON m.id = d.id
AND d.user_id = 275
AND d.status = 'deleted'
WHERE d.id IS NULL
GROUP BY m.thread_id
) q ON q.id = t.id
LEFT JOIN messages r ON r.id = q.id
The second LEFT JOIN operation here is used to retrieve the CreatedDate value of the newest undeleted message from the messages table.
I'm getting this error :
Query Error : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ';WITH convs AS ( select c.id, c.title, c.seen, c.id_receiver, c.id_send' at line 1
when i use this query :
$query = ";WITH convs AS (
select c.id, c.title, c.seen, c.id_receiver, c.id_sender
from conversations c
)
select id, title, seen, id_receiver, id_sender
from convs
where id_receiver = '5'
order by title desc limit 0,25";
$res = mysqli_query($connection ,$query);
Am i missing something ?
Your help would be much appreciated.
PS : i minimised the query to make it simple for this context, if you help me find the solution, i may have another problem with the full query. So i might come back to you for more help. Thank's in advance.
EDIT (WHOLE QUERY)
$query = "WITH convs AS (
select c.id, c.title, c.seen, c.id_receiver, c.id_sender,
(select max(date) from messages where id_conversation = c.id and id_user <> '$iduser') as last_msg,
(select top 1 id_user from messages where id_conversation = c.id and id_user <> '$iduser' order by date desc) as last_user,
(select count(distinct id_user) from messages where id_conversation = c.id) as nbruser,
(select count(*) from messages where id_conversation = c.id) as nbrmsg,
(select username from users where id = c.id_sender) as sender, (select username from users where id = c.id_receiver) as receiver,
(select count(*) from deleted_conversations where id_user='$iduser' and id_conversation=c.id) as deleted,
from conversations c
)
select id, title, seen, id_receiver, id_sender, receiver, sender, last_msg, last_user, deleted, nbruser, nbrmsg
from convs
where (id_receiver = '$iduser' or (id_sender == '$iduser' and nbruser > 1)) and deleted = 0
order by last_msg desc limit $pageLimit,$REC_PER_PAGE";
What pushed me to use CTE is the need of using aliases in where clause. And as you can see i have many of them.
Can you give me an example of how to use views/temporary tables to achieve my purpose ?
MySQL/MariaDB doesn't support CTEs. Plus, it is entirely unnecessary in this case:
select id, title, seen, id_receiver, id_sender
from conversations c
where id_receiver = '5'
order by ?? desc
limit 0, 25;
Note: You need to specify the column for the order by as well.
For more complex examples, you can use subqueries, views, and/or temporary tables.
CTEs are quite similar to Derived Tables:
select id, title, seen, id_receiver, id_sender, receiver, sender, last_msg, last_user, deleted, nbruser, nbrmsg
FROM
(
select c.id, c.title, c.seen, c.id_receiver, c.id_sender,
(select max(date) from messages where id_conversation = c.id and id_user <> '$iduser') as last_msg,
(select top 1 id_user from messages where id_conversation = c.id and id_user <> '$iduser' order by date desc) as last_user,
(select count(distinct id_user) from messages where id_conversation = c.id) as nbruser,
(select count(*) from messages where id_conversation = c.id) as nbrmsg,
(select username from users where id = c.id_sender) as sender, (select username from users where id = c.id_receiver) as receiver,
(select count(*) from deleted_conversations where id_user='$iduser' and id_conversation=c.id) as deleted,
from conversations c
) as convs
where (id_receiver = '$iduser' or (id_sender == '$iduser' and nbruser > 1)) and deleted = 0
order by last_msg desc limit $pageLimit,$REC_PER_PAGE
i need select some data from two tables ,
please help me use inner join for this selection .
players in selction2 must not be in selection1...
first select :
$rs = "SELECT *
FROM `player`
WHERE `status`=1 AND `credit`>=1 AND `username` NOT LIKE '$user'
ORDER BY ls ASC,credit DESC
LIMIT 0 ,10;
Second: this players must remove from result of selection1
$rs2 = "SELECT *
FROM `ip_log`
WHERE `playerid`='$ui' AND `win`='1' AND `date`='$date' ";`
You can use LEFT JOIN for this:
This shows the log messages for everyone not in selection 1.
SELECT l.*
FROM ip_log AS l
LEFT JOIN
(SELECT username
FROM player
WHERE status = 1 AND credit >= 1 AND username NOT LIKE '$user'
ORDER BY ls ASC, credit DESC
LIMIT 10) AS p
ON l.player = p.username
WHERE win = 1 and date = '$date'
AND p.username IS NULL
This shows the top 10 player data, except the ones with log messages in selection 2
SELECT p.*
FROM player AS p
LEFT JOIN ip_log AS l ON l.player = p.username AND l.win = 1 AND l.date = '$date'
WHERE p.status = 1 AND p.credit >= 1 AND p.username NOT LIKE '$user'
AND l.player IS NULL
ORDER BY p.ls ASC, p.credit DESC
LIMIT 10
In both cases, testing a column in the second table with IS NULL makes it return only the rows in the first table that don't have a match in the second table. See
Return row only if value doesn't exist
You can do it with LEFT JOIN
SELECT player.*,ip_log.* FROM `player` LEFT JOIN `ip_log` ON player.id!=ip_log.playerid GROUP BY player.id
I`m trying to extract the activity of a user with a single query. I need to do it with one single query so I can use zend paginator.
I have 5 tables: users, replies, threads, wiki_articles and wiki_article_revisions. Each table has 2 common columns created_by and created_on.
I've tried using left join but I think what it returns is not correct and I'm unable to order all activity by created_on
Here is the join I've tried:
SELECT * FROM `users` u
LEFT join `replies` r ON u.id = r.created_by
LEFT join `threads` t ON u.id = t.created_by
LEFT join `wiki_articles` wa ON u.id = wa.created_by
LEFT join `wiki_article_revisions` war ON u.id = war.created_by
WHERE (u.`name` = 'CGeorges')
My thought process was wrong. I should have used UNION for this, like:
(SELECT id, name, created_on FROM users WHERE id = 1)
UNION
(SELECT id, name, created_on FROM threads WHERE created_by = 1)
UNION
(SELECT id, content, created_on FROM replies WHERE created_by = 1)
UNION
(SELECT id, title, created_on FROM wiki_articles WHERE created_by = 1)
ORDER by created_on DESC
I would really appreciate any help trying to simplify a MySQL query. The purpose of the query is to retrieve messages from a messages table (users_messages) which has the following columns: message_id, from_id, to_id, message_content, date_sent.
The from_id and to_id need to join a users table (users) which has these columns: user_id, user_username.
Also I should mention that there is a blocked users table (users_blocked) which filters out any messages should the user_id feature in this table.
All this works fine and messages are ordered with the newest first which is what I want. My only problem is that it's not pulling the corresponding 'message_content'. i.e. it's pulling the most recent date, but not the most recent message.
Perhaps I need a different approach (e.g. subqueries) but I can not get my head around it.
Here is the query:
select m.message_content,
if(from_id < to_id, concat(from_id,to_id), concat(to_id,from_id)) as ft,
if (from_id = $my_id, to_id, from_id) as other_id,
max(date_sent) as most_recent
from users_messages m
left join users_blocked ub1 on (from_id = ub1.blocked_id and ub1.user_id = $my_id)
left join users_blocked ub2 on (to_id = ub2.blocked_id and ub2.user_id = $my_id)
where
(from_id = $my_id or to_id = $my_id)
and ub1.blocked_id is null
and ub2.blocked_id is null
group by
ft
order by
most_recent desc
Sorry, here are the table structures:
users
user_id user_username
1 Simon
2 Amber
3 Tom
users_messages
message_id from_id to_id date_sent message_content
1 1 2 2012-07-04 11:52:12 Hello
2 1 2 2012-07-04 12:32:24 Another message
3 1 2 2012-07-04 14:00:00 Hello again
users_blocked
user_id blocked_id
1 3
Try:
select m.message_content,
x.ft,
x.other_id,
x.most_recent
from (select if(from_id < to_id, concat(from_id,to_id), concat(to_id,from_id)) as ft,
if(from_id = $my_id, to_id, from_id) as other_id,
max(date_sent) as most_recent
from users_messages um
left join users_blocked ub1
on (um.from_id = ub1.blocked_id and ub1.user_id = $my_id)
left join users_blocked ub2
on (um.to_id = ub2.blocked_id and ub2.user_id = $my_id)
where ub1.blocked_id is null and ub2.blocked_id is null and
(um.from_id = $my_id or um.to_id = $my_id)
group by ft) x
join users_messages m
on m.date_sent = x.most_recent and
m.from_id in ($my_id, x.other_id) and
m.to_id in ($my_id, x.other_id)
order by
x.most_recent desc
SQLFiddle here.
Here I am assuming SimonKing wants, message content from users_messages table which includes following condition,
Users should not blocked either directions,
The most recent messages transferred between the users
So that, I modified Mark Bannister query as follows,
SELECT temp.* FROM (
SELECT um.*, concat(um.from_id,to_id) as direction FROM userMessages um
LEFT JOIN userBlocked ub1 ON um.from_id = ub1.user_id AND um.to_id = ub1.blocked_id
LEFT JOIN userBlocked ub2 ON um.to_id = ub2.user_id AND um.from_id = ub2.blocked_id
WHERE ub1.user_id is null AND ub1.blocked_id is null AND ub2.user_id is null AND ub2.blocked_id is null
ORDER BY um.date_sent DESC
) temp
GROUP BY direction
SQL fiddle is http://sqlfiddle.com/#!2/bdc77/1/0
As i understand, the main problem of this request is that result contains only first dates, not messages. To fix this you can do this:
make prepared data set which will have most recent dates:
select to_id, from_id, max(date_sent) as most_recent
from users_messages m
left join users_blocked ub on ub.user_id = $my_id
and ub.blocked_id in (to_id, from_id)
where
(from_id = $my_id or to_id = $my_id)
and ub.blocked_id is null
group by
to_id, from_id
order by
most_recent desc
I see you groping data by two collumns to_id, from_id. This subquery isn't best place to calculate things like:
if(from_id < to_id, concat(from_id,to_id), concat(to_id,from_id)) as ft
Then just select other needed data from users_messages, which matches our to_id, from_id and recent_date from prepared table:
select um.* from
(
select to_id, from_id, max(date_sent) as most_recent
from users_messages m
left join users_blocked ub on ub.user_id = 1
and ub.blocked_id in (to_id, from_id)
where
(from_id = 1 or to_id = 1)
and ub.blocked_id is null
group by
to_id, from_id
order by
most_recent desc
) as prepared_messages
left join users_messages um on um.from_id = prepared_messages.from_id
and um.to_id = prepared_messages.to_id
and um.date_sent = prepared_messages.most_recent