How would I avoid using two separate queries for this purpose? - mysql

I am using MySQL to create a database of articles and categories. Each article has a category. I would like to make a feature for the admin panel that lists all the categories, but also includes the latest article for each category. The method I usually use is to fetch rows from the category table, loop through the results, and then create another query using something like FROM articlesWHERE category_id = {CATEGORY_ID} ORDER BY article_id DESC LIMIT 1. That method just seems like overkill to me and I am wondering if it can be done in one query(Maybe with joins and subqueries?).
This is the current query I have that fetches categories:
SELECT * FROM article_categories ORDER BY category_title ASC
These are my tables:
CREATE TABLE IF NOT EXISTS `articles` (
`article_id` int(15) NOT NULL AUTO_INCREMENT,
`author_id` int(15) NOT NULL,
`category_id` int(15) NOT NULL,
`modification_id` int(15) NOT NULL,
`title` varchar(125) NOT NULL,
`content` text NOT NULL,
`type` tinyint(1) NOT NULL,
`date_posted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL,
`attachment_id` int(15) NOT NULL,
`youtube_id` varchar(32) DEFAULT NULL,
`refs` text NOT NULL,
`platforms` varchar(6) NOT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `article_categories` (
`category_id` int(15) NOT NULL AUTO_INCREMENT,
`parent_id` int(15) NOT NULL,
`title` varchar(50) NOT NULL,
`description` text NOT NULL,
`attachment_id` text NOT NULL,
`enable_comments` tinyint(1) NOT NULL,
`enable_ratings` tinyint(1) NOT NULL,
`guest_reading` tinyint(1) NOT NULL,
`platform_assoc` tinyint(1) NOT NULL,
`allowed_types` varchar(6) NOT NULL,
PRIMARY KEY (`category_id`,`title`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
This is the query I have come up with so far:
SELECT
c.category_id, c.title, c.description,
a.article_id, a.category_id, a.title, COUNT(a.article_id) AS total_articles
FROM article_categories AS c
LEFT JOIN articles AS l ON (
SELECT
article_id AS article_id, category_id, title AS article_title
FROM articles AS l
WHERE l.category_id = c.category_id
ORDER BY l.article_id
DESC LIMIT 1)
LEFT JOIN articles AS a ON (c.category_id = a.category_id)
GROUP BY c.category_id
ORDER BY c.title ASC
The above query gives me the following SQL error:
Operand should contain 1 column(s)
Why is this happening?

You can return list of all the categories and recent article in each category using one query, Try this
SELECT C.*, A.*
FROM article_categories C
LEFT OUTER JOIN articles A ON c.category_id = A.category_id
WHERE
(
A.category_id IS NULL OR
A.article_id = (SELECT MAX(X.article_id)
FROM articles X WHERE X.category_id = C.category_id)
)

This will restrict the articles to just the highest article_id per category and make use of the indexes on those tables:
select
ac.category_id, ac.title, newa.article_id, newa.title article_title
from article_categories ac
left join articles newa on ac.category_id = newa.category_id
left join articles olda on newa.category_id = olda.category_id
and olda.article_id > newa.article_id
where olda.article_id is null
;
See this Demonstrated at SQLFiddle

Shoelace, I was browsing your other questions and saw that this was unresolved so I've decided to take a crack at it.
This is a little tricky, but I don't think it's too bad, assuming I understand your question correctly. First, get the latest article date for each category:
SELECT a.category_id, MAX(a.date_posted)
FROM articles a
JOIN article_categories c ON c.category_id = a.category_id
GROUP BY a.category_id;
Then, join that with your articles table on the condition that the category_id and date are equal and you have what you need:
SELECT ar.*
FROM articles ar
JOIN(SELECT a.category_id, MAX(a.date_posted) AS latestDateForCategory
FROM articles a
JOIN article_categories c ON c.category_id = a.category_id
GROUP BY a.category_id) t
ON t.category_id = ar.category_id AND t.latestDateForCategory = ar.date_posted;
SQL Fiddle.

Related

mysql: inner join with in and not in

I'm trying to pull rows from one table "articles" based on specific category tags from table "article_category_reference", to exclude articles that have a specific tag. I have this query right now:
SELECT DISTINCT
a.article_id,
a.`title`,
a.`text`,
a.`date`
FROM
`articles` a
INNER JOIN `article_category_reference` c ON
a.article_id = c.article_id AND c.`category_id` NOT IN (54)
WHERE
a.`active` = 1
ORDER BY
a.`date`
DESC
LIMIT 15
The problem is, it seems to grab rows even if they do have a row in the "article_category_reference" table where "category_id" matches "54". I've also tried it in the "where" clause and it makes no difference.
Keep in mind I'm using "NOT IN" as it may be excluding multiple tags.
SQL fiddle to show it: http://sqlfiddle.com/#!9/b2172/1
Tables:
CREATE TABLE `article_category_reference` (
`ref_id` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `articles` (
`article_id` int(11) UNSIGNED NOT NULL,
`author_id` int(11) UNSIGNED NOT NULL,
`date` int(11) NOT NULL,
`title` varchar(120) NOT NULL,
`text` text CHARACTER SET utf8mb4 NOT NULL,
`active` int(1) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
One option is to use an EXISTS clause:
SELECT DISTINCT
a.article_id,
a.title,
a.text,
a.date
FROM articles a
WHERE
a.active = 1 AND
NOT EXISTS (SELECT 1 FROM article_category_reference c
WHERE a.article_id = c.article_id AND c.category_id = 54)
ORDER BY
a.date DESC
LIMIT 15;
The logical problem with your current approach of checking the category in the WHERE clause is that it is checking individual records. You need to assert that all category records for a given article, in aggregate, do not match the category you wish to exclude. An EXISTS clause, as I have written above, is one way to do it. Using GROUP BY in a subquery is another way.
The NOT IN condition is evaluated for each joined rows. Since you have same article_id with multiple category_id-values, the ones that do match the NOT IN condition will get picked.
See SQLFiddle.
To select articles that do not have any rows with category_id 54 use a subquery:
SELECT
a.article_id,
a.`title`,
a.`text`,
a.`date`
FROM `articles` a
WHERE a.`active` = 1 AND
a.`article_id` not in (
SELECT c.article_id
FROM `article_category_reference` c
WHERE c.`category_id` = 54
)
ORDER BY a.`date`
DESC
LIMIT 15

Left join does not meet my requirement

There are two tables over here: first is for basic details of "poll" and
second for the "answer". My requirement is like this:
i want poll records to display only for users who didn't answer the poll
I tried this query but it is not working:
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on (p.b_id != pr.b_poll_id
And pr.b_user_id !=14) where p.v_status = 'enable'
And p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17' order by p.b_id limit 1"
CREATE TABLE IF NOT EXISTS `mycom_poll` (
`b_id` bigint(20) NOT NULL AUTO_INCREMENT,
`b_user_id` bigint(20) NOT NULL DEFAULT '0',
`v_code` varchar(50) NOT NULL,
`v_question` varchar(1000) CHARACTER SET utf8 NOT NULL,
`v_country` varchar(100) NOT NULL,
`d_activate_date` date NOT NULL,
`dt_created_date` datetime NOT NULL,
`dt_updated_date` datetime NOT NULL,
`v_ip` varchar(20) NOT NULL,
`v_status` varchar(20) NOT NULL DEFAULT 'enable',
PRIMARY KEY (`b_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
INSERT INTO `mycom_poll` (`b_id`, `b_user_id`, `v_code`, `v_question`, `v_country`, `d_activate_date`, `dt_created_date`, `dt_updated_date`, `v_ip`, `v_status`) VALUES(8, 0, '20150417115406-192168113C5ZJpXu2hM', 'Who is chief minstor of Gujarat in 2015 Rajyasabha?', '["Global","India","United States"]', '2015-04-15', '2015-04-17 11:56:12', '2015-04-18 07:29:11', '127.0.0.1', 'enable');
CREATE TABLE IF NOT EXISTS `mycom_polls_result` (
`b_id` bigint(20) NOT NULL AUTO_INCREMENT,
`b_poll_id` bigint(20) NOT NULL,
`b_user_id` bigint(20) NOT NULL,
`b_poll_answer` bigint(20) NOT NULL,
`dt_created_date` datetime NOT NULL,
`dt_updated_date` datetime NOT NULL,
`v_ip` varchar(20) NOT NULL,
`v_status` varchar(20) NOT NULL DEFAULT 'enable',
PRIMARY KEY (`b_id`),
KEY `b_poll_id` (`b_poll_id`,`b_user_id`),
KEY `b_poll_id_2` (`b_poll_id`,`b_user_id`,`b_poll_answer`),
KEY `b_poll_id_3` (`b_poll_id`,`b_user_id`,`b_poll_answer`),
KEY `b_user_id` (`b_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `mycom_polls_result` (`b_id`, `b_poll_id`, `b_user_id`, `b_poll_answer`, `dt_created_date`, `dt_updated_date`, `v_ip`, `v_status`) VALUES
(1, 8, 14, 64, '2015-04-18 13:27:50', '2015-04-18 13:27:50', '127.0.0.1', 'enable');
Your query:
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on (p.b_id != pr.b_poll_id
And pr.b_user_id !=14) where p.v_status = 'enable'
And p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17' order by p.b_id limit 1
First I am going to point out the mistake of this query, what you are doing here is, you are left joining two tables on id of first table not equals to id of second table which will join all the table data whose ids are not same, when you do a left join, you always get the data of left table and if data not present in right table you get null.
So in where clause you should check any if the field of second table is null or not like this.
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on p.b_id = pr.b_poll_id
where pr.b_user_id !=14 p.v_status = 'enable'
And p.v_country like '%India%' AND p.d_activate_date <= '2015-04-17'
and pr.b_poll_id is null order by p.b_id limit 1
I request you to understand left join first.
At the moment you are using LEFT JOIN which is correct to force all rows of the mycom_poll table to be considered, but you're joining on p.b_id != pr.b_poll_id which will associate each poll row in mycom_poll with all the unrelated results rows in mycom_polls_result that don't belong to it.
You probably need to join on p.b_id = pr.b_poll_id, and then require rows with pr.b_poll_id IS NULL in your WHERE clause.
You also seem to be trying to restrict results to pr.b_user_id !=14, which is fine, but that needs to be stated along with other filtering conditions in the WHERE clause. The LEFT JOIN ON clause is just going to be used to declare the column relationships between your tables.
SELECT p.*
FROM mycom_poll as p
LEFT JOIN mycom_polls_result as pr
ON p.b_id = pr.b_poll_id
WHERE
pr.b_user_id !=14
AND p.v_status = 'enable'
AND p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17'
AND pr.b_poll_id IS NULL
ORDER BY p.b_id LIMIT 1
HTH

How would I get articles from a certain category and its children?

I'm developing an article system that uses categories and child categories.
Basically, if the category has a parent_id value, it's a child of that category.
I would like to be able to get the most recent articles from a category and articles from its child categories.
For example: I have a category called "Gaming Articles" and several child categories under that called Xbox, PlayStation, Nintendo, and PC. My system makes it possible to post articles in the parent categories such as Gaming Articles as well as in the child categories.
So this would have to include articles that are in either the parent category or the child categories of that parent.
CREATE TABLE IF NOT EXISTS `articles` (
`article_id` int(15) NOT NULL AUTO_INCREMENT,
`author_id` int(15) NOT NULL,
`category_id` int(15) NOT NULL,
`modification_id` int(15) NOT NULL,
`title` varchar(125) NOT NULL,
`content` text NOT NULL,
`date_posted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL,
`attachment_id` int(15) NOT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `article_categories` (
`category_id` int(15) NOT NULL AUTO_INCREMENT,
`parent_id` int(15) NOT NULL,
`title` varchar(50) NOT NULL,
`description` text NOT NULL,
`attachment_id` text NOT NULL,
`enable_comments` tinyint(1) NOT NULL,
`enable_ratings` tinyint(1) NOT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
The query I have so far...
SELECT article_id, category_id
FROM articles
WHERE category_id = 1
ORDER BY article_id DESC
LIMIT 10
Of course, this only gets articles under that category, not from both the category and that category's child categories.
As your table structure stands, this query will work assuming only 1 level of nesting (ie children don't themselves have children):
SELECT a.*
FROM articles a
JOIN article_categories ac ON a.category_id = ac.category_id
WHERE 1 IN (a.category_id, ac.parent_id)
ORDER BY a.article_id DESC
LIMIT 10
Note the "reversed" style IN to neatly capture what is effectively an OR.
If your nesting is deeper, simply add another join for each level, for example if you have up to 4 levels (2 more than the above query):
SELECT a.*
FROM articles a
JOIN article_categories ac1 ON a.category_id = ac1.category_id
LEFT JOIN article_categories ac2 ON ac1.parent_id = ac2.category_id
LEFT JOIN article_categories ac3 ON ac2.parent_id = ac3.category_id
WHERE 1 IN (a.category_id, ac1.parent_id, ac2.parent_id, ac3.parent_id
ORDER BY a.article_id DESC
LIMIT 10
In the second case, the use of left joins is necessary to still return articles that don't have so many levels above.
In this structure is inposible in one query. (I assume that there are many levels of categories)
You can:
recursively search child categories (ex. in php) and then
SELECT * FROM articles WHERE category_id IN ( $categories );
change db structure and use tree structure
try: The Nested Set Model in article : http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
add new column to categories table, and store full path to category, ex.:
category 1->2, category_path 1,2
category 1->2->3, category_path 1,2,3
category 1->4, new category_path 1,4
If You looking all data in category 1 and children, try:
SELECT
a.*
FROM articles a
INNER JOIN categories c
ON a.category_id = c.category_id
WHERE c.category_path LIKE '1,%'
As far as i understand, what you want is to get all the articles from a child of a category, if that is correct, try this out:
SELECT a.article_id, a.category_id FROM articles as a, article_categories c WHERE a.category_id = c.category_id AND c.parent_id = (SELECT c.parent_id WHERE c.category_id = 1) ORDER BY article_id DESC LIMIT 10
If this wasn't what you wanted, comment it, i'll try to answer you.

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.