MySQL Group, Join and Select Max - mysql

First of all I know similar questions has been asked here, I've checked but could not find a proper solution to my issue.
I have these two tables (trimming to only required parts for simplicity):
CREATE TABLE messages(
message_id MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
message_conversation_id MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0',
message_from MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0',
message_to MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0',
message_text TEXT NOT NULL DEFAULT '',
PRIMARY KEY (message_id),
KEY message_conversation_id (message_conversation_id),
KEY message_from (message_from),
KEY message_to (message_to)
) ENGINE=INNODB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE users (
user_id MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
user_name VARCHAR(400) NOT NULL DEFAULT '',
PRIMARY KEY (user_id)
) ENGINE=INNODB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
I want make a query, which shows maximum message_id's message_text, and user's info (which may be both stored at message_from or message_to, and filtered with a where clause (simply: message will come to me, and provide me the other user's user info, because in that conversation last message may both be from me or the other guy in conversation) (which made me stuck on solving)).
This query is what I came up with so far:
SELECT `m`.*, `u`.*, `u2`.*
FROM (`messages` AS m)
INNER JOIN `users` AS u ON `u`.`user_id`=`m`.`message_from`
INNER JOIN `users` AS u2 ON `u2`.`user_id`=`m`.`message_to`
WHERE (m.message_from="1" OR m.message_to="1")
AND `u`.`user_id` != '1'
AND `u2`.`user_id` != '1'
GROUP BY `m`.`message_conversation_id`
ORDER BY `m`.`message_id` desc
This query shows exactly what I need (I guess), except it provides lowest value from messages table.
How can I do this ? Where am I doing wrong ?
Thanks,

Here is an alternative way to get latest message of each conversation.
SELECT m.*, u.*, u2.*
FROM (`messages` AS m)
LEFT JOIN users AS u ON u.user_id=m.message_from
LEFT JOIN users AS u2 ON u2.user_id=m.message_to
WHERE (m.`message_from` != 1 AND m.`message_to` != 1) AND m.`message_id` IN (
SELECT MAX(`message_id`)
FROM `messages`
GROUP BY `message_conversation_id`
HAVING `message_id` = m.`message_id`)
ORDER BY m.message_id DESC;
EXPLAIN this, the result is "Using where; Using index", means "mission accomplished".

I got what I needed this way, What I needed is at the message_body result column. Is this healthy, or are there any other (possibly better) ways?
SELECT DISTINCT(m.message_conversation_id),max(m.message_id), m.*,u.*, u2.*,
(SELECT m2.message_text FROM messages m2 WHERE m2.message_id = max(m.message_id)) AS 'message_body'
FROM (`messages` AS m)
LEFT JOIN users AS u ON u.user_id=m.message_from
LEFT JOIN users AS u2 ON u2.user_id=m.message_to
WHERE (m.message_from='1' OR m.message_to='1')
GROUP BY m.message_conversation_id
ORDER BY m.message_id DESC

Related

I'm at a dead end. SQL Query to get the last replied username and time stamp

This is the query I am trying to get to work.
SELECT
t.id AS topic_id,
t.title AS topic_title,
t.content AS topic_content,
t.created_at AS topic_created_at,
t.updated_at AS topic_updated_at,
t.user_id AS topic_user_id,
c.id AS comment_id,
c.content AS comment_content,
c.created_at AS comment_created_at,
max_c.username AS comment_username,
u.username AS topic_username
FROM
topics t
JOIN
(SELECT
c2.topic_id, c2.created_at, u2.username
FROM
comments c2
JOIN
users u2 ON c2.user_id = u2.id
JOIN
topics t2 ON c2.topic_id = t2.id
ORDER BY
c2.created_at desc) max_c ON t.id = max_c.topic_id
JOIN
comments c ON max_c.created_at = c.created_at
JOIN
users u ON u.id = t.user_id
ORDER BY
c.created_at DESC
Pretty sure this part of the query is not correct:
SELECT
c2.topic_id, c2.created_at, u2.username
FROM
comments c2
JOIN
users u2 ON c2.user_id = u2.id
JOIN
topics t2 ON c2.topic_id = t2.id
ORDER BY
c2.created_at desc
That query currently displays the following. But I want to group by created_at or what ever is suitable so we only get the latest replied on topic.
If you can help this would be amazing I've spent about 5 hours trying to write this so far...
I've attached my table migrations below.
# Dump of table comments
# ------------------------------------------------------------
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` varchar(255) DEFAULT NULL,
`user_id` int(11) NOT NULL,
`topic_id` int(11) DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `comments_ibfk_2` (`topic_id`),
CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
CONSTRAINT `comments_ibfk_2` FOREIGN KEY (`topic_id`) REFERENCES `topics` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# Dump of table topics
# ------------------------------------------------------------
CREATE TABLE `topics` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`content` text,
`user_id` int(11) NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
CONSTRAINT `topics_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# Dump of table users
# ------------------------------------------------------------
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT 'NOT NULL',
`email` varchar(255) DEFAULT 'NOT NULL',
`password` char(60) DEFAULT 'NOT NULL',
`admin` int(11) NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
If I understand your question the problem you are having is the subquery should return only the last username and timestamp, but returns all instead. That being the case you could just use LIMIT 1 after the order by to have desired result.
SELECT c2.topic_id, c2.created_at,u2.username
FROM comments c2
JOIN users u2 ON c2.user_id = u2.id
JOIN topics t2 ON c2.topic_id = t2.id
ORDER BY c2.created_at desc
LIMIT 1
Please try the following...
SELECT topics.id AS topic_id,
topics.title AS topic_title,
topics.content AS topic_content,
topics.created_at AS topic_created_at,
topics.updated_at AS topic_updated_at,
topicsUsers.user_id AS topic_user_id,
topicsUsers.username AS topic_username,
comments.id AS comment_id,
comments.content AS comment_content,
maxCreatedAt AS comment_created_at,
commentsUsers.user_id AS comment_user_id
commentsUsers.username AS comment_username,
FROM topics
JOIN ( SELECT topic_id AS topic_id,
MAX( comments.created_at ) AS maxCreatedAt,
FROM comments
GROUP BY topic_id
) maxCreatedAtFinder ON topics.id = maxCreatedAtFinder.topic_id
JOIN comments ON maxCreatedAtFinder.maxCreatedAt = comments.created_at
AND maxCreatedAtFinder.topic_id = topics.id
JOIN users AS topicsUsers ON users.id = topics.user_id
JOIN users AS commentsUsers ON users.id = comments.user_id
ORDER BY maxCreatedAt DESC
As best I can tell from your Question, you are looking for a list that shows the details of each topic (including the user details of the user who initiated the Topic) along with the most recent comment for that Topic and the details of the user who posted that comment.
My statement follows a similar structure to the statement you supplied. I have removed from the subquery the JOIN to topics since the topic_id is the only detail from topics that we want for the subquery, and topic_id can also be obtained from comments.
Obtaining the details of the user who posted the most recent comment for the Topic would complicate the grouping and result in more joins being performed than if we joined Comments to Users outside of the subquery. All the subquery really needs to do is obtain the most recent value of created_at for each Topic. As such I have removed the JOIN to Users and the associated field selections.
In the main statement I have maintained the INNER JOIN of the subquery to topics, which effectively appends the maximum value of created_at from comments to its corresponding record from topics.
I then take the resulting dataset and join it to comments in such a way that a topic and its associated most recent value of created_at from comments has the contents of each comment for that topic with that created_at value appended to it. Please note that whilst unlikely it is possible for two comments belonging to a topic to be created at the same time and thus to have the same value of created_at. Both such records will be joined to the dataset being formed. In the absence of instructions to the contrary I have assumed that is desired behaviour and have allowed it.
I then take the resulting dataset and JOIN it to two instances of users, one based on the topic_id and the other based on the user_id for the comment. This has the effect of appending to each record in our dataset the user details for the user who created the Topic and the user details of the user(s) who posted the most recent comment(s).
This final dataset is then sorted into chronological order, starting with the most recent record(s).
The desired fields are then selected and returned by the statement.
If you have any questions or comments, then please feel free to post a Comment accordingly.

MysqL big table query optimization

I have a chatting application. I have an api which returns list of users who the user talked. But it takes a long to mysql return a list messages when it reachs 100000 rows of data.
This is my messages table
CREATE TABLE IF NOT EXISTS `messages` (
`_id` int(11) NOT NULL AUTO_INCREMENT,
`fromid` int(11) NOT NULL,
`toid` int(11) NOT NULL,
`message` text NOT NULL,
`attachments` text NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '0',
`date` datetime NOT NULL,
`delete` varchar(50) NOT NULL,
`uuid_read` varchar(250) NOT NULL,
PRIMARY KEY (`_id`),
KEY `fromid` (`fromid`,`toid`,`status`,`delete`,`uuid_read`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=118561 ;
and this is my users table (simplified)
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`login` varchar(50) DEFAULT '',
`sex` tinyint(1) DEFAULT '0',
`status` varchar(255) DEFAULT '',
`avatar` varchar(30) DEFAULT '0',
`last_active` datetime DEFAULT NULL,
`active` tinyint(1) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=15523 ;
And here is my query (for user with id 1930)
select SQL_CALC_FOUND_ROWS `u_id`, `id`, `login`, `sex`, `birthdate`, `avatar`, `online_status`, SUM(`count`) as `count`, SUM(`nr_count`) as `nr_count`, `date`, `last_mesg` from
(
(select `m`.`fromid` as `u_id`, `u`.`id`, `u`.`login`, `u`.`sex`, `u`.`birthdate`, `u`.`avatar`, `u`.`last_active` as online_status, COUNT(`m`.`_id`) as `count`, (COUNT(`m`.`_id`)-SUM(`m`.`status`)) as `nr_count`, `tm`.`date` as `date`, `tm`.`message` as `last_mesg` from `messages` as m inner join `messages` as tm on `tm`.`_id`=(select MAX(`_id`) from `messages` as `tmz` where `tmz`.`fromid`=`m`.`fromid`) left join `users` as u on `u`.`id`=`m`.`fromid` where `m`.`toid`=1930 and `m`.`delete` not like '%1930;%' group by `u`.`id`)
UNION
(select `m`.toid as `u_id`, `u`.`id`, `u`.`login`, `u`.`sex`, `u`.`birthdate`, `u`.`avatar`, `u`.`last_active` as online_status, COUNT(`m`.`_id`) as `count`, 0 as `nr_count`, `tm`.`date` as `date`, `tm`.`message` as `last_mesg` from `messages` as m inner join `messages` as tm on `tm`.`_id`=(select MAX(`_id`) from `messages` as `tmz` where `tmz`.`toid`=`m`.`toid`) left join `users` as u on `u`.`id`=`m`.`toid` where `m`.`fromid`=1930 and `m`.`delete` not like '%1930;%' group by `u`.`id`)
order by `date` desc ) as `f` group by `u_id` order by `date` desc limit 0,10
Please help to optimize this query
What I need,
Who user talked to (name, sex, and etc)
What was the last message (from me or to me)
Count of messages (all)
Count of unread messages (only to me)
The query works well, but takes too long.
The output must be like this
You have some design problems on your query and database.
You should avoid keywords as column names, as that delete column or the count column;
You should avoid selecting columns not declared in the group by without an aggregation function... although MySQL allows this, it's not a standard and you don't have any control on what data will be selected;
Your not like construction may cause a bad behavior on your query because '%1930;%' may match 11930; and 11930 is not equal to 1930;
You should avoid like constructions starting and ending with % wildcard, which will cause the text processing to take longer;
You should design a better way to represent a message deletion, probably a better flag and/or another table to save any important data related with the action;
Try to limit your result before the join conditions (with a derived table) to perform less processing;
I tried to rewrite your query the best way I understood it. I've executed my query in a messages table with ~200.000 rows and no indexes and it performed in 0,15 seconds. But, for sure you should create the right indexes to help it perform better when the amount of data increase.
SELECT SQL_CALC_FOUND_ROWS
u.id,
u.login,
u.sex,
u.birthdate,
u.avatar,
u.last_active AS online_status,
g._count,
CASE WHEN m.toid = 1930
THEN g.nr_count
ELSE 0
END AS nr_count,
m.`date`,
m.message AS last_mesg
FROM
(
SELECT
MAX(_id) AS _id,
COUNT(*) AS _count,
COUNT(*) - SUM(m.status) AS nr_count
FROM messages m
WHERE 1=1
AND m.`delete` NOT LIKE '%1930;%'
AND
(0=1
OR m.fromid = 1930
OR m.toid = 1930
)
GROUP BY
CASE WHEN m.fromid = 1930
THEN m.toid
ELSE m.fromid
END
ORDER BY MAX(`date`) DESC
LIMIT 0, 10
) g
INNER JOIN messages AS m ON 1=1
AND m._id = g._id
LEFT JOIN users AS u ON 0=1
OR (m.fromid <> 1930 AND u.id = m.fromid)
OR (m.toid <> 1930 AND u.id = m.toid)
ORDER BY m.`date` DESC
;

How can this complex query be simplified? [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have a media center website that I'm working on that has a page that displays media categories. Each category can have multiple media items assigned to it, and each media item can be assigned to multiple categories.
The page contains a text input that must be able to filter the categories that are shown.
What I need to be able to do is get every category that is associated with the current user ($user_id) as well as a media item belonging to that user (unused categories are not displayed). Normally that would be simple enough, but I also have to be able to filter the categories based on fields in other tables associated with the media.
The fields I need to be able to apply the text filter to are as follows:
message_number in the media table
keywords in the media table
speaker_name in the media_speakers table
series_name in the media_series table
book_name in the media_books table
category_name in the media_categories table
As it is now, the query takes several seconds to complete. I'm not a MySQL pro, so I'm sure there must be better ways to do what I need to do here. In case it helps, I'm using MySQLi via PHP. My query has several subqueries, which I'm positive is the cause of the problem, but I didn't know any other way to do what I'm trying to do.
Below are the relevant table structures and the current query. I've included as much info as I can think of that can help someone to help me with this, but if you need more info please just let me know.
The media table (omitting some irrelevant fields) (series,speaker, and book fields contain the ID of a record in their corresponding tables):
`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`date` date NOT NULL DEFAULT '0000-00-00',
`message_number` varchar(32) DEFAULT NULL,
`series` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
`speaker` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
`book` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
`keywords` text NOT NULL,
PRIMARY KEY (`id`)
The media_series table:
`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`series_name` varchar(255) NOT NULL DEFAULT '',
`cover` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
The media_speakers table:
`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`speaker_name` varchar(255) NOT NULL DEFAULT '',
`cover` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
The media_books table:
`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
`book_name` varchar(64) NOT NULL DEFAULT '',
`book_shortname` varchar(10) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
The media_categories table:
`id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`category_name` varchar(255) NOT NULL DEFAULT '',
`cover` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id` (`id`)
The media_categories_assoc table:
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned DEFAULT NULL,
`media_id` int(10) unsigned zerofill DEFAULT NULL,
`category_id` int(10) unsigned zerofill DEFAULT NULL,
`marked_for_deletion` int(1) DEFAULT '0',
PRIMARY KEY (`id`)
Finally, the over-complicated query:
SELECT media_categories.id `media_categories.id`,
media_categories.user_id `media_categories.user_id`,
media_categories.category_name `media_categories.category_name`,
media_categories.cover `media_categories.cover`,
(SELECT id
FROM media
WHERE user_id = '$user_id'
AND media_categories.id IN (SELECT category_id
FROM media_category_assoc
WHERE user_id = '$user_id')
ORDER BY `date` DESC
LIMIT 1) `media.id`,
(SELECT `date`
FROM media
WHERE user_id = '$user_id'
AND media_categories.id IN (SELECT category_id
FROM media_category_assoc
WHERE user_id = '$user_id')
ORDER BY `date` DESC
LIMIT 1) `media.date`,
(SELECT series
FROM media
WHERE user_id = '$user_id'
AND media_categories.id IN (SELECT category_id
FROM media_category_assoc
WHERE user_id = '$user_id')
ORDER BY `date` DESC
LIMIT 1) `media.series`,
(SELECT speaker
FROM media
WHERE user_id = '$user_id'
AND media_categories.id IN (SELECT category_id
FROM media_category_assoc
WHERE user_id = '$user_id')
ORDER BY `date` DESC
LIMIT 1) `media.speaker`
FROM media_categories
LEFT JOIN media
ON media.id IN (SELECT media_id
FROM media_category_assoc
WHERE media_id = media.id
AND user_id = '$user_id')
LEFT JOIN media_series
ON media.series = media_series.id
LEFT JOIN media_speakers
ON media.speaker = media_speakers.id
LEFT JOIN media_books
ON media.book = media_books.id
WHERE media_categories.user_id = '$user_id'
AND media_categories.id IN (SELECT category_id
FROM media_category_assoc
WHERE user_id = '$user_id')
AND ( media.title LIKE '%filter_text%'
OR media.message_number LIKE '%filter_text%'
OR media.keywords LIKE '%filter_text%'
OR media_speakers.speaker_name LIKE '%filter_text%'
OR media_categories.category_name LIKE '%filter_text%'
OR media_series.series_name LIKE '%filter_text%'
OR media_books.book_name LIKE '%filter_text%' )
GROUP BY `media_categories.id`
ORDER BY `media.date` DESC
LIMIT 0, 12;
If I have been able to understand the query correctly you try to get some extra information from the most recent media per category for a given user. As far as I can see all of the subqueries in the SELECT-clause can then be moved to the FROM-clause.
Maybe doing it like this could help ?
SELECT media_categories.id,
media_categories.user_id,
media_categories.category_name,
media_categories.cover,
newest_media.id,
newest_media.'date',
newest_media.series,
newest_media.speaker
FROM media_categories
LEFT JOIN media_category_assoc
ON media_categories.id = media_category_assoc.category_id AND media_categories.user_id = media_category_assoc.user_id
LEFT JOIN (
SELECT id, 'date', series, speaker
FROM media
WHERE media.id = media_category_assoc.media_id
ORDER BY `date` DESC
LIMIT 1
) newest_media ON newest_media.user_id = '$user_id'
LEFT JOIN media_series
ON newest_media.series = media_series.id
LEFT JOIN media_speakers
ON newest_media.speaker = media_speakers.id
LEFT JOIN media_books
ON newest_media.book = media_books.id
LEFT JOIN media
ON media.id = media_category_assoc.media_id AND media.user_id = '$user_id'
WHERE media_categories.user_id = '$user_id'
AND ( media.title LIKE '%filter_text%'
OR media.message_number LIKE '%filter_text%'
OR media.keywords LIKE '%filter_text%'
OR media_speakers.speaker_name LIKE '%filter_text%'
OR media_categories.category_name LIKE '%filter_text%'
OR media_series.series_name LIKE '%filter_text%'
OR media_books.book_name LIKE '%filter_text%' )
GROUP BY `media_categories.id`
ORDER BY `media.date` DESC
LIMIT 0, 12;
As I mentioned in my comment, the subqueries can be the bottlenecks in your query. First of all, run an explain select... on your query to check the execution plan.
See the reference manual:
http://dev.mysql.com/doc/refman/5.0/en/explain.html
http://dev.mysql.com/doc/refman/5.0/en/using-explain.html
Now, about the suggestion I made about using temporary tables, I'll take your first subquery to make an example.
You use this:
SELECT
...,
(SELECT id
FROM media
WHERE user_id = '$user_id'
AND media_categories.id IN (SELECT category_id
FROM media_category_assoc
WHERE user_id = '$user_id')
ORDER BY `date` DESC
LIMIT 1),
....
And you can do something like this:
drop table if exists temp_step1;
create temporary table temp_step1
select id
from media
where user_id = #user_id -- I'm assuming you are putting this in a stored procedure
and media_categories.id in (SELECT category_id
FROM media_category_assoc
WHERE user_id = #user_id)
order by `date` desc
limit 1;
Then you can use this temp_step1 table as a row source for your big query.
Notice that this example returns only one row, so there's no point on indexing this. For those temp tables that contain more than one row and which you use in the FROM ... JOIN ... clause of your query, you will need to create indexes at the very least on all the fields you are doing the joins. To do that, after creating the temp table (for example temp_step_X) you should do this:
alter table temp_step_X
add index idx_indexName(field1),
...;
Hope this helps you

How to join two tables without messing up the query

I have this query for example (good, it works how I want it to)
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments`
GROUP BY `discusComments`.`memberID` ORDER BY postcount DESC
Example Results:
memberid postcount
3 283
6 230
9 198
Now I want to join the memberid of the discusComments table with that of the discusTopic table (because what I really want to do is only get my results from a specific GROUP, and the group id is only in the topic table and not in the comment one hence the join.
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments`
LEFT JOIN `discusTopics` ON `discusComments`.`memberID` = `discusTopics`.`memberID`
GROUP BY `discusComments`.`memberID` ORDER BY postcount DESC
Example Results:
memberid postcount
3 14789
6 8678
9 6987
How can I stop this huge increase happening in the postcount? I need to preserve it as before.
Once I have this sorted I want to have some kind of line which says WHERE discusTopics.groupID = 6, for example
CREATE TABLE IF NOT EXISTS `discusComments` (
`id` bigint(255) NOT NULL auto_increment,
`topicID` bigint(255) NOT NULL,
`comment` text NOT NULL,
`timeStamp` bigint(12) NOT NULL,
`memberID` bigint(255) NOT NULL,
`thumbsUp` int(15) NOT NULL default '0',
`thumbsDown` int(15) NOT NULL default '0',
`status` int(1) NOT NULL default '1',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=7190 ;
.
CREATE TABLE IF NOT EXISTS `discusTopics` (
`id` bigint(255) NOT NULL auto_increment,
`groupID` bigint(255) NOT NULL,
`memberID` bigint(255) NOT NULL,
`name` varchar(255) NOT NULL,
`views` bigint(255) NOT NULL default '0',
`lastUpdated` bigint(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `groupID` (`groupID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=913 ;
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments`
JOIN `discusTopics` ON `discusComments`.`topicID` = `discusTopics`.`id`
GROUP BY `discusComments`.`memberID` ORDER BY postcount DESC
Joining the topicid in both tables solved the memberID issue. Thanks #Andiry M
You need to use just JOIN not LEFT JOIN and you can add AND discusTopics.memberID = 6 after ON discusComments.memberID = discusTopics.memberID
You can use subqueries lik this
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments` where `discusComments`.`memberID` in
(select distinct memberid from `discusTopics` WHERE GROUPID = 6)
If i understand your question right you do not need to use JOIN here at all. JOINs are needed in case when you have many to many relationships and you need for each value in one table select all corresponding values in another table.
But here you have many to one relationship if i got it right. Then you can simply do select from two tables like this
SELECT a.*, b.id FROM a, b WHERE a.pid = b.id
This is simple request and won't create a giant overhead as JOIN does
PS: In the future try to experiment with your queries, try to avoid JOINs especially in MySQL. They are slow and dangerous in their complexity. For 90% of cases when you want to use JOIN there is simple and much faster solution.

LEFT JOIN not working as expected with sub-query

I've got the SQL query below:
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
It returns all the rows (about 4000) in the messages table with additional columns coming from the numbers table. So far, this is what I would expect.
Now I left join this sub-query to another table, again using a left join:
SELECT message, sent_date
FROM (
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id
However, it only returns about 200 rows so many are missing. Since this is a left join I would expect all the rows from table1 to be in the result. Can anybody see what the issue is?
Edit:
So for information here are the 3 relevant tables (with irrelevant columns removed):
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message` text CHARACTER SET utf8 NOT NULL,
`from_id` int(11) DEFAULT NULL,
`sent_date` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `from_id` (`from_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=101553 ;
CREATE TABLE IF NOT EXISTS `numbers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`number` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6408 ;
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2395 ;
You can try alternative method to debug the issue:
CREATE TEMPORARY table tmp1 AS SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers
ON messages. from_id = numbers.id;
and then see whether this query works.
SELECT message, sent_date
FROM tmp1 table1
LEFT JOIN users
ON table1.user_id = users.id;
Also for your case make sure that there are no other insert or updates in between. otherwise use transactions.
table1 sometimes won't have a UserID - so that'll be null, so those results will be missing?
I don't have an exact answer to your question, but if I have to start thinking, I will first find out what 3800 rows are missing and try to see the pattern (is it because user_id are null or duplicate)
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
MINUS
(SELECT table1.message, table1.sent_date, table1.user_id
FROM (
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id)
Try this, I think it's a scoping issue on user_id.
SELECT table1.message, table1.sent_date
FROM (
SELECT messages.message, messages.sent_date, numbers.user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id
I'm not sure if user_id is in messages or numbers.
There is no way this should happen.
Try this variation:
SELECT
m.message, m.sent_date, n.user_id
FROM
messages m
LEFT JOIN
numbers AS n ON m.from_id = n.id
LEFT JOIN
users AS u ON n.user_id = u.id ;