Combine two MYSQL SELECT queries [duplicate] - php

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to combine two Post/Category tables MYSQL SELECT queries into one
I have two MYSQL queries:
1) "SELECT ID,post_title,post_category,post_perma FROM ".TBL_POSTS."
WHERE published='1' AND page='0' ORDER BY ID ASC LIMIT 50"
2) "SELECT p.cat_ID,p.cat_nicename FROM ".TBL_CATEGORIES." n,
".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft
AND p.rgt AND n.cat_ID='".post_category."' ORDER BY p.lft
I use it like this:
$sql="SELECT ID, post_title, post_category,post_perma
FROM ".TBL_POSTS."
WHERE published='1'
AND page='0'
ORDER BY ID ASC LIMIT 50";
$result=mysql_query($sql);
while($row=mysql_fetch_assoc($result)){
$sql2="SELECT p.cat_ID, p.cat_nicename
FROM ".TBL_CATEGORIES." n, ".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft
AND p.rgt
AND n.cat_ID='".$row['post_category']."'
ORDER BY p.lft";
$result2=mysql_query($sql2);
while($row2=mysql_fetch_assoc($result2)){
$path.='/'.$row2['cat_nicename'];
}
$link.=''.$row['post_title'].'<br>';
$path='';
}
echo($link);
exit;
}
This is how I get path in the link.. now What i want is:
I want to combine both queries so, that I do not have run the second query in while loop ..
It gets very bad because if I receive 100 posts.. second query will run 100 times to fetch the path.

You can JOIN the two tables like so:
SELECT
p.ID,
p.post_title,
c.cat_nicename,
p.post_perma
FROM TBL_POSTS p
INNER JOIN TBL_CATEGORIES c
ON p.CategoryID = c.Cat_ID
WHERE p.published = '1'
AND p.page = '0'
ORDER BY p.ID ASC
LIMIT 50

Try to use MySql JOIN or UNION

You can use a UNION to combine all of them into a single query.
Really, you would need to do something like this so that all of the columns don't get combined into each other. Also, both select statements need to have the same number of columns being selected.
EDIT: Also, in MySQL UNION's you can only have 1 order by and it has to be in the last SELECT, so you will have to adjust your query accordingly.
(SELECT ID,
post_title,
post_category,
post_perma,
NULL AS cat_id,
NULL AS cat_nicename
FROM ".TBL_POSTS."
WHERE published='1'
AND page='0')
ORDER BY ID ASC LIMIT 50
UNION
(SELECT
NULL AS ID,
NULL AS post_title,
NULL AS post_category,
NULL AS post_perma,
p.cat_ID,
p.cat_nicename
FROM ".TBL_CATEGORIES." n,
".TBL_CATEGORIES." p
WHERE n.cat_ID='".post_category."'
AND n.lft BETWEEN p.lft AND p.rgt)
ORDER BY p.lft

Related

MySql inner join takes more than 10 seconds

I have two tables posts and followings
posts (id,userid,post,timestamp) 30 000 rows
and
followings(id_me,userid) 90 000 rows
I want to get lattest 10 posts form posts table based on the people i follow and my posts
SELECT p.*
FROM posts as p INNER JOIN
followings as f
ON (f.id_me=(my user id) AND p.userid=f.userid )
OR
p.userid=(my user id)
ORDER BY id DESC LIMIT 10
But it takes about 10-15 seconds to return. Thanks in advance!
First, remove the filter from the join clause, let the join just correlate the joining tables.
(
SELECT p.*
FROM posts as p
INNER JOIN followings as f ON p.userid=f.userid
where f.id_me=(my user id)
UNION
SELECT p.*
FROM posts as p
where p.userid=(my user id)
)
ORDER BY id DESC LIMIT 10
second, verify your indexes if that ids got no indexes it ill perform a full table scan for each cartesian product of both tables (30k x 90k =~ 3700k pairs being compared)
third, if you don't follow yourself you need a union from post you are following and your posts
Using an OR in SQL is a performance killer, try this:
SELEC p.*
FROM posts as p INNER JOIN
followings as f
ON (f.id_me=(my user id) AND p.userid IN (f.userid,(my user id)))
ORDER BY id DESC LIMIT 10
Do this query using union:
(SELECT p.*
FROM posts p INNER JOIN
followings f
ON (f.id_me=(my user id) AND p.userid=f.userid
)
union
(select p.*
from posts p
where p.userid=(my user id)
)
ORDER BY id DESC
LIMIT 10
If the two conditions never overlap, then use union all instead.
An OR condition like that prevents the query optimizer from making use of indexes. Use a UNION instead:
SELECT *
FROM (SELECT p.*
FROM posts as p
INNER JOIN followings as f
ON f.id_me=(my user id) AND p.userid=f.userid
UNION
SELECT *
FROM posts
WHERE userid = (my user id)) u
ORDER BY id DESC
LIMIT 10
It might be just me but I think your WHERE clause is in an inefficient location:
SELECT
p.*
FROM
posts p
INNER JOIN
followings f
ON p.userid=f.userid
WHERE
MyUserID IN (p.userid, f.id_me)
ORDER BY
id DESC
LIMIT
10
I read in comments that you have the required indexes. The problem is the query. Combining OR with a JOIN confuses the poor and (often) dumb optimizer. The LIMIT 10 should be helpful but the optimizer is not (yet) smart enough to make the best plan.
Try this query:
( SELECT p.*
FROM posts AS p
JOIN followings AS f
ON f.id_me = (my_user_id)
AND p.userid = f.userid
ORDER BY p.id DESC
LIMIT 10
)
UNION ALL
( SELECT p.*
FROM posts AS p
WHERE p.userid = (my_user_id)
ORDER BY p.id DESC
LIMIT 10
) AS x
ORDER BY id DESC
LIMIT 10 ;

How to combine two Post/Category tables MYSQL SELECT queries into one

I have two MySQL queries:
1) "SELECT ID,post_title,post_category,post_perma FROM ".TBL_POSTS."
WHERE published='1' AND page='0' ORDER BY ID DESC LIMIT 10"
2) "SELECT p.cat_ID,p.cat_nicename FROM ".TBL_CATEGORIES." n, ".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft AND p.rgt AND n.cat_ID='".post_category."' ORDER BY p.lft
First query selects posts and then second select the Path of the category by post_category please note that post_category will be taken from first query means post_category is common in both table.. in first table it is named as post_category and in second it is cat_ID
Right now I am running it in foreach loop which is not good. Also one thing to be noticed that second query will also return Array and that one array should correspond to post_category
Can any SQL expert help me?
Many Thanx
Please try this it might be helpful to you.
SELECT a.ID, a.post_title,a.post_category,a.post_perma, b.cat_ID, b.cat_nickname
FROM (SELECT ID,post_title,post_category,post_perma FROM ".TBL_POSTS." WHERE published='1' AND page='0' ORDER BY ID DESC LIMIT 10) a
LEFT JOIN (SELECT p.cat_ID as cat_ID,p.cat_nicename as cat_nickname FROM " . TBL_CATEGORIES . " n, " . TBL_CATEGORIES . " p WHERE n.lft BETWEEN p.lft AND p.rgt AND n.cat_ID = '" .$post_category. "' ORDER BY p.lft) b ON a.ID = b.cat_ID
I would use LEFT JOIN
Like this:
$sql = "SELECT `p`.`ID`,`p`.`post_title`,`p`.`post_category`,`p`.`post_perma`,`c`.`cat_ID`,`c`.`cat_nicename` FROM `".TBL_POSTS."` AS `p` ";
$sql .= "LEFT JOIN `".TBL_CATEGORIES."` AS `c` ON `c`.`cat_ID`=`p`.`post_category` WHERE `p`.`published`='1' AND `p`.`page`='0' ORDER BY `p`.`ID` DESC LIMIT 10";
You may need to tweak the WHERE clause to suite your needs more..
Please Note: This is one string, i have just split them onto two lines so it is easier to read. .= is to append the current string.

MYSQL return rows with number of column relationships in another table

I have a table categories and table posts . I want to return categories that have more than 3 posts.
My query
SELECT `categories`.`category_title`, COUNT(posts.post_id) as total_posts
FROM (`categories`)
JOIN `posts` ON `posts`.`category_id` = `categories`.`category_id`
HAVING `total_posts` > 3
ORDER BY `categories`.`date_created` desc
it returns just 1 row.. What is the correct way to do this type of query without using 2 queries?
Your query is making use of a MySQL feature called "hidden columns" and you might not even know it. This is because your query is referencing elements, such as date_created, which should be aggregated but are not ("should" here means according to the SQL standard and most other databases).
The problem with your query is that it is missing the group by. An alternative way of writing this is with the aggregation in a subquery, before joining to category:
SELECT `categories`.`category_title`, total_posts
FROM `categories` JOIN
(select categoryid, COUNT(posts.post_id) as total_posts
from `posts`
group by categoryid
having count(*) > 3
) pc
ON `pc`.`category_id` = `categories`.`category_id`
ORDER BY `categories`.`date_created` desc
You need to group the items by category.
SELECT `categories`.`category_title`, COUNT(posts.post_id) as total_posts
FROM (`categories`)
JOIN `posts` ON `posts`.`category_id` = `categories`.`category_id`
GROUP BY `categories`.`category_id`
HAVING `total_posts` > 3
ORDER BY `categories`.`date_created` desc

Sort a MySQL result based on total count of another result

I have tables tbl_posts and tbl_comments with primary keys post_id and comment_id respectively. I tried this code:
$allPosts=mysql_query("
select c.comment_id, post_id, count(*)
from post u,
comments c
where u.comment_id = c.comment_id
group by comment_id, post_id
LIMIT 10
");
but I have no clue what it does. How do I combine two tables so that the total comments determines the order of the listed posts from tbl_posts?
Try this, it's more readable if you separate per lines and work with joins
select c.comment_id, post_id, count(*)
from post u join comments c
on u.comment_id = c.comment_id
group by comment_id, post_id LIMIT 10
It looks like you have tables named tbl_comment and tbl_post but your query has them listed as just comment and post.
select c.comment_id, post_id, count(*)
from tbl_post u, tbl_comments c
where u.comment_id = c.comment_id
group by comment_id, post_id LIMIT 10
$allPosts=mysql_query("select c.comment_id, post_id, count(*) from tbl_post u, tbl_comments c where u.comment_id = c.comment_id group by comment_id, post_id LIMIT 10");
This just fixes the query so it runs, and does not address any content issues you may have, namely the group by on both (what I am guessing) are primary keys.
** EDIT **
To fix the sorting try:
SELECT tbl_post.comment_id, count(*)
FROM tbl_post, tbl_comments
WHERE tbl_post.comment_id = tbl_comment.comment_id
GROUP BY comment_id LIMIT 10
ORDER BY count(*)
Explanation of your SQL:
You are selecting column comment_id from table comments, column post_id from table post using a inner join, grouping by comment_id, post_id, with a limit of 10 results.
I would try:
$allPosts = mysql_query("SELECT * FROM
(SELECT c.comment_id, u.post_id, COUNT(*) AS 'count' FROM post u
LEFT JOIN comments c ON c.comment_id = u.comment_id
GROUP BY c.comment_id, u.post_id)
ORDER BY count DESC LIMIT 10");

PHP/MySQL Query In a loop, how to append to just one query?

I have a query to pull all articles out of the database..
$get_articles = $db->query("
SELECT *, a.id AS id, s.type AS type FROM ".$prefix."articles a
LEFT JOIN ".$prefix."sources s ON (s.id = source_id)
WHERE a.type!='trashed' ORDER BY a.timestamp DESC LIMIT $start, $end");
Within the loop of this query, I do then do another query on the same table to find related articles to the 'title' of the article, stored as '$related_phrase'. The query within the loop is:
// get related articles to this entry
$get_related = $db->query("
SELECT *, a.id AS id, MATCH (title, description) AGAINST ('$related_phrase') AS score FROM ".$prefix."articles a
LEFT JOIN ".$prefix."sources s ON (s.id = source_id) WHERE a.type!='trashed' AND MATCH (title, description) AGAINST ('$related_phrase') AND a.id!='$articles[id]' HAVING score > 7
ORDER BY a.timestamp DESC LIMIT 0, 3");
This basically means we have a query in a loop which is causing the pages to load very slowly.
What we want to do, is bring the query from within the loop, in the main query, so it's all working within one query, if that's possible?
Any help very much appreciated!
I don't think you would gain much speed by merging the two queries.
One thing you could try is to get a list of all articles and DISTINCT searchphrases (in e.g. temptable), and then build a query to get all related articles in one single go. Lastly match up related articles with the article list.
try this:
$articles_and_related = $db->query("
SELECT *
FROM ".$prefix."articles art
LEFT JOIN (
SELECT * FROM ".$prefix."articles x
WHERE
score > 7
AND x.type != 'trashed'
AND x.id != art.id
AND MATCH(x.title, x.description) AGAINST (art.title)
LIMIT 3
) rel
LEFT JOIN ".$prefix."sources s2 ON (s2.id = rel.source_id)
LEFT JOIN ".$prefix."sources s ON (s.id = art.source_id)
WHERE
art.type!='trashed'
ORDER BY art.timestamp DESC LIMIT $start, $end");

Categories