Select value that is not "*username*" - mysql

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...

Related

Optimization of relatively basic JOIN and GROUP BY query

I have a relatively basic query that fetches the most recent messages per conversation:
SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message`
LEFT JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id`
ORDER BY `max_add_time` DESC
LIMIT 12
The message table contains more than 911000 records, the conversation table contains around 680000. The execution time for this query, varies between 4 and 10 seconds, depending on the load on the server. Which is far too long.
Below is a screenshot of the EXPLAIN result:
The cause is apparently the MAX and/or the GROUP BY, because the following similar query only takes 10ms:
SELECT COUNT(*)
FROM `message`
LEFT JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE (`message`.`status`=0)
AND (`message`.`user_id` <> 1)
AND ((`conversation`.`sender_user_id` = 1 OR `conversation`.`receiver_user_id` = 1))
The corresponding EXPLAIN result:
I have tried adding different indices to both tables without any improvement, for example: conv_msg_idx(add_time, conversation_id) on message which seems to be used according to the first EXPLAIN result, however the query still takes around 10 seconds to execute.
Any help improving the indices or query to get the execution time down would be greatly appreciated.
EDIT:
I have changed the query to use an INNER JOIN:
SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message`
INNER JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id`
ORDER BY `max_add_time` DESC
LIMIT 12
But the execution time is still ~ 6 seconds.
You should create Multiple-Column Index on the columns which are in your WHERE clause and which you want to SELECT (except conversation_id). (reference)
conversation_id should be an index in both table.
Try to avoid 'Or' in Sql query this will make the fetching slow. Instead use union or any other methods.
SELECT message.conversation_id, MAX(message.add_time) AS max_add_time FROM message INNER JOIN conversation ON message.conversation_id = conversation.id WHERE (conversation.sender_user_id = 1 AND conversation.status != -1)) GROUP BY conversation_id
union
SELECT message.conversation_id, MAX(message.add_time) AS max_add_time FROM message INNER JOIN conversation ON message.conversation_id = conversation.id WHERE ((conversation.receiver_user_id = 1 AND conversation.status != -2) ) GROUP BY conversation_id ORDER BY max_add_time DESC LIMIT 12
Instead of depending on a single table message, have two tables: One for message, as you have, plus another thread that keeps the status of the thread of messages.
Yes, that requires a little more work when adding a new message -- update a column or two in thread.
But it eliminates the GROUP BY and MAX that are causing grief in this query.
While doing this split, see if some other columns would be better off in the new table.
SELECT `message`.`conversation_id`, MAX(`message`.`add_time`) AS `max_add_time`
FROM `message`
INNER JOIN `conversation` ON `message`.`conversation_id` = `conversation`.`id`
WHERE ((`conversation`.`receiver_user_id` = 1 AND `conversation`.`status` != -2)
OR (`conversation`.`sender_user_id` = 1 AND `conversation`.`status` != -1))
GROUP BY `conversation_id`
ORDER BY `max_add_time` DESC
LIMIT 12
You can try with INNER JOIN, if your logic not get affect using it.
you can modify this query by avoiding max() use
select * from(
select row_number() over(partition by conversation_id order by add_time desc)p1
)t1 where t1.p1=1

Using mysql to find common set between two lists

I have two queries in which I would like to find their common values. I'm trying to ultimately find out what percentage of users have visited both webpages.
SELECT DISTINCT user_id
FROM table
WHERE url ='y'
ORDER BY user_id;
SELECT DISTINCT user_id
FROM table
WHERE url ='z'
ORDER BY user_id;
I've tried a
NOT IN
and a
UNION
but haven't had much luck - though I could easily be doing it wrong. I'm new.
One method is to use conditional aggregation. To get information for each user:
select user_id,
sum(url = 'y') as y_visits,
sum(url = 'z') as z_visits
from t
group by user_id;
To get the list of users, add a having clause:
having y_visits >= 1 and z_visits >- 1
To get summary information:
select y_visitor, z_visitor, count(*)
from (select user_id,
max(url = 'y') as y_visitor,
max(url = 'z') as z_visitor
from t
group by user_id
) yz
group by y_visitor, z_visitor;
To get a simple percentage:
select avg(y_visitor = 1 and z_visitor = 1) as p_VisitedBothYandZ
from (select user_id,
max(url = 'y') as y_visitor,
max(url = 'z') as z_visitor
from t
group by url
) yz;

MySQL: Getting multiple rows based on the value of one row

My MySQL table looks like the following:
I would like to get results where all the followings are satisfied:
form_id is 1
sort by lead_id high to low
field_number 9 for that lead_id must be 111
In summary, my query should return 9 rows. Is that possible?
Here is my attempt so far, but I really want to avoid using GROUP_CONCAT if at all possible to get a cleaner result set.
SELECT lead_id, GROUP_CONCAT(field_number, "|", value SEPARATOR "----") AS `values`
FROM lead_detail
WHERE form_id = 1
GROUP BY lead_id
ORDER BY lead_id DESC
If you are meaning that you want to include only rows for a lead_id if there exists a row for that lead_id with field_number=9 and value=111. And otherwise, all rows for that lead_id should be excluded...
You could do something like this:
SELECT d.id
, d.lead_id
, d.form_id
, d.field_number
, d.value
FROM ( SELECT e.lead_id
FROM lead_detail e
WHERE e.form_id = 1
AND e.field_number = 9
AND e.value = 111
GROUP BY e.lead_id
) f
JOIN lead_detail d
ON d.lead_id = f.lead_id
AND d.form_id = 1
ORDER BY d.lead_id DESC
The inline view (aliased as f) returns a distinct list of lead_id that meet specific criteria. We can reference the result from that like it were a table, and use that in a JOIN operation, to return "matching" rows from the lead_detail table. (If there's no row with value=111, field_number=9, and form_id=1 for a particular lead_id, then the inline view won't return that lead_id in the list.)
As another alternative, we could use an EXISTS predicate with a correlated subquery, but this may not perform as well:
SELECT d.id
, d.lead_id
, d.form_id
, d.field_number
, d.value
FROM lead_detail d
WHERE d.form_id = 1
AND EXISTS
( SELECT 1
FROM lead_detail e
WHERE e.form_id = 1
AND e.field_number = 9
AND e.value = 111
AND e.lead_id = d.lead_id
)
ORDER BY d.lead_id DESC
That essentially says, for every row from lead_detail run the subquery following the EXIST keyword... if the subquery returns one (or more) rows, then the EXISTS predicate returns TRUE, otherwise it returns FALSE. That subquery is "correlated" to the outer query, by a predicate in the WHERE clause, matching the lead_id to the value of lead_id from the row from the outer query.
The short answer is "not easily." There are a couple of ways to do this that aren't super straightforward -- probably the easiest is with a sub-query (the exact format will change depending on the exact format of your table, etc.)
SELECT lead_id, GROUP_CONCAT(field_number, "|", value SEPARATOR "----") AS `values`
FROM lead_detail
WHERE `form_id` = 1
AND `lead_id` = (SELECT `lead_id` FROM `lead_detail` WHERE `field_number` = 9 AND `value` = 111 AND `form_id` = 1) /*This is the subquery*/
GROUP BY lead_id
ORDER BY lead_id DESC
Other options include running two separate queries, one to pull the form_id (or form_ids) in question, the other to pull all data for that form_id (or those form_ids).

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.

MYSQL: Help Querying Results From Three Tables

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