7 query with inner join into 1 query, mysql - php

i have a question, how do i make totally 7 query into 1 query? to make the db query lesser? i have 1 table contain all of it, but the suggest is a "separator", each suggest i have to load 33 rows and order by dateline, i have think about use any inner join ... etc, but i think that is not a way? correct me if i'm wrong. Would Appreciate for help!THanks!!
This is the mysql query 1
$query = DB::query("SELECT t.*, d.did AS dingid, d.id AS dingpid, f.id AS bookmark, f.uid AS buid
FROM ".DB::table('comeing_tao')." AS t
LEFT JOIN ".DB::table('comeing_tao_ding')." AS d ON t.id = d.id AND d.uid = ".$_G['uid']."
LEFT JOIN ".DB::table('comeing_tao_fans')." AS f ON t.id = f.id AND f.uid = ".$_G['uid']."
WHERE t.suggest = 0 AND t.state = 1 ORDER BY dateline DESC LIMIT 33");
This is the mysql query 2
$query = DB::query("SELECT t.*, d.did AS dingid, d.id AS dingpid, f.id AS bookmark, f.uid AS buid
FROM ".DB::table('comeing_tao')." AS t
LEFT JOIN ".DB::table('comeing_tao_ding')." AS d ON t.id = d.id AND d.uid = ".$_G['uid']."
LEFT JOIN ".DB::table('comeing_tao_fans')." AS f ON t.id = f.id AND f.uid = ".$_G['uid']."
WHERE t.suggest = 1 AND t.state = 1 ORDER BY dateline DESC LIMIT 33");
This is the mysql query 3
$query = DB::query("SELECT t.*, d.did AS dingid, d.id AS dingpid, f.id AS bookmark, f.uid AS buid
FROM ".DB::table('comeing_tao')." AS t
LEFT JOIN ".DB::table('comeing_tao_ding')." AS d ON t.id = d.id AND d.uid = ".$_G['uid']."
LEFT JOIN ".DB::table('comeing_tao_fans')." AS f ON t.id = f.id AND f.uid = ".$_G['uid']."
WHERE t.suggest = 2 AND t.state = 1 ORDER BY dateline DESC LIMIT 33");
This is the mysql query 3
$query = DB::query("SELECT t.*, d.did AS dingid, d.id AS dingpid, f.id AS bookmark, f.uid AS buid
FROM ".DB::table('comeing_tao')." AS t
LEFT JOIN ".DB::table('comeing_tao_ding')." AS d ON t.id = d.id AND d.uid = ".$_G['uid']."
LEFT JOIN ".DB::table('comeing_tao_fans')." AS f ON t.id = f.id AND f.uid = ".$_G['uid']."
WHERE t.suggest = 3 AND t.state = 1 ORDER BY dateline DESC LIMIT 33");
This is the mysql query 4
$query = DB::query("SELECT t.*, d.did AS dingid, d.id AS dingpid, f.id AS bookmark, f.uid AS buid
FROM ".DB::table('comeing_tao')." AS t
LEFT JOIN ".DB::table('comeing_tao_ding')." AS d ON t.id = d.id AND d.uid = ".$_G['uid']."
LEFT JOIN ".DB::table('comeing_tao_fans')." AS f ON t.id = f.id AND f.uid = ".$_G['uid']."
WHERE t.suggest = 4 AND t.state = 1 ORDER BY dateline DESC LIMIT 33");
and so on... i skip the following 3 query cos is totally same except the t.suggest = 5, t.suggest = 6, t.suggest = 7
the goal is all the query in one, then play around with the array.

you can use OR
(t.suggest=3 OR t.suggest=4 OR ...) AND ...
if you want max 33 records from each suggest, try UNION
(SELECT ...) UNION (SELECT ...) UNION (SELECT ...) ...

TRY in clause
SELECT
t.*, d.did AS dingid, d.id AS dingpid, f.id AS bookmark, f.uid AS buid
FROM
".DB::table('comeing_tao')." AS t
LEFT JOIN ".DB::table('comeing_tao_ding')." AS d ON t.id = d.id AND d.uid =
".$_G['uid']."
LEFT JOIN ".DB::table('comeing_tao_fans')." AS f ON t.id = f.id AND f.uid =
".$_G['uid']."
WHERE
t.suggest IN(0,1,2,3,4,5,6,7)
AND
t.state = 1
ORDER BY
dateline DESC
but Limit will not give 33 records for each but 33 for whole return

Related

Get latest comments from multiple tables

I'm having trouble with getting the 3 latest comments from two different tables.
Here is my code, which works perfect with one inner join:
$query = mysql_query("SELECT COUNT(c.topic_id) AS ctid, COUNT(c.deck_id) AS dtid, f.id AS forumid, f.class AS forumclass, f.name AS forumname, f.url AS forumurl,
c.id AS commentid, c.user_id AS commentuser, c.user_name AS commentusername, c.date AS commentdate,
c.topic_id AS topicid, c.deck_id AS deckid
FROM ".$prefix."comment AS c
INNER JOIN ".$prefix."forum AS f
ON c.topic_id = f.id GROUP BY f.id
ORDER BY commentdate DESC LIMIT 3") or die(mysql_error());
This works fine it shows the 3 latest comments from the forum table, however I have comments in the decks table too, but when I add another JOIN to the query it just won't work anymore.
$query = mysql_query("SELECT COUNT(c.topic_id) AS ctid, COUNT(c.deck_id) AS dtid, f.id AS forumid, f.class AS forumclass, f.name AS forumname, f.url AS forumurl,
c.id AS commentid, c.user_id AS commentuser, c.user_name AS commentusername, c.date AS commentdate,
c.topic_id AS topicid, c.deck_id AS deckid, , d.id, d.url AS deckurl, d.name AS deckname
FROM ".$prefix."comment AS c
INNER JOIN ".$prefix."forum AS f
ON c.topic_id = f.id
INNER JOIN ".$prefix."decks AS d
ON c.deck_id = d.id
GROUP BY f.id ORDER BY commentdate DESC LIMIT 3") or die(mysql_error());
There's a comment table and in the comment table there is a topic_id column, which is equal to the forum table's id column and there's also a deck_id column which is equal to the decks table's id column.
Obviously that GROUP BY f.id isn't good with the two inner joins.
After this query I have while ($top = mysql_fetch_assoc($query)){ ... and then if ($top['deckid']==0) then print the topicid informations else print the deckid informations.
EDIT:
Comment table (only what is important to us now):
id, topic_id, deck_id
topic_id = forum table's id
deck_id = deck table's id
Obviously there is no comment to every forum topic.
E.g.: Forum ID 5 has 4 comments, then comment table e.g.: ID 1,2,3,4 has topic_id 4,4,4,4 and deck_id 0,0,0,0.
If there's no comment then there's nothing in the comment table. So forum ID 6 has 0 comments, then there is nothing in the comments table.
If deck ID 12 has 2 comments, then comment table e.g.: 5,6 has deck_id 12,12 and topic_id 0,0.
Forum table:
id
Deck table:
id
EDIT2: Solution (not too nice, but it works):
//count how many comments the latest 3 deck topic has
$new_comment_query = mysql_query("SELECT COUNT(c.deck_id) AS dtid, c.id, c.deck_id, c.date, d.id
FROM ".$prefix."comment AS c LEFT JOIN ".$prefix."decks AS d ON d.id = c.deck_id GROUP BY d.id ORDER BY date DESC LIMIT 3");
$new_one = mysql_fetch_array($new_comment_query);
//count how many comments the latest 3 forum topic has
$new_forum_query = mysql_query("SELECT COUNT(c.topic_id) AS ctid, c.id, c.topic_id, c.date, f.id
FROM ".$prefix."comment AS c LEFT JOIN ".$prefix."forum AS f ON f.id = c.topic_id GROUP BY f.id ORDER BY date DESC LIMIT 3");
$newer_one = mysql_fetch_array($new_forum_query);
//get all the comments
$comment_query = mysql_query("SELECT id, topic_id, deck_id, date FROM ".$prefix."comment ORDER BY date DESC LIMIT 3");
while ($comment = mysql_fetch_assoc($comment_query))
{
if($comment['topic_id']==0)
{
$deck_query = mysql_query("SELECT * FROM ".$prefix."decks WHERE id=".$comment['deck_id']);
while ($deck_comments = mysql_fetch_assoc($deck_query))
{
//print all the things!
}
}
elseif($comment['deck_id']==0)
{
$forum_query = mysql_query("SELECT * FROM ".$prefix."forum WHERE id=".$comment['topic_id']);
while ($forum_comments = mysql_fetch_assoc($forum_query))
{
//print all the things!
}
}
}
Try running this query:
SELECT * FROM
(
SELECT COUNT(c.topic_id) AS ctid, COUNT(c.deck_id) AS dtid,
f.id AS forumid, f.class AS forumclass, f.name AS forumname,
f.url AS forumurl,
c.id AS commentid, c.user_id AS commentuser, c.user_name AS commentusername,
c.date AS commentdate, c.topic_id AS topicid, c.deck_id AS deckid,
d.id, d.url AS deckurl, d.name AS deckname
FROM ".$prefix."comment AS c
INNER JOIN ".$prefix."forum AS f
ON c.topic_id = f.id
INNER JOIN ".$prefix."decks AS d
ON c.deck_id = d.id
ORDER BY commentdate DESC
) t1
GROUP BY t1.forumid
LIMIT 3
I first execute your complex JOIN without the GROUP BY which was causing problems. Then I SELECT everything from that temporary table, grouping by the forumid.
In all honesty, I'm surprised your server wasn't barfing on your first query, let alone the second, but hopefully my answer will help you out.
//count how many comments the latest 3 deck topic has
$new_comment_query = mysql_query("SELECT COUNT(c.deck_id) AS dtid, c.id, c.deck_id, c.date, d.id
FROM ".$prefix."comment AS c LEFT JOIN ".$prefix."decks AS d ON d.id = c.deck_id GROUP BY d.id ORDER BY date DESC LIMIT 3");
$new_one = mysql_fetch_array($new_comment_query);
//count how many comments the latest 3 forum topic has
$new_forum_query = mysql_query("SELECT COUNT(c.topic_id) AS ctid, c.id, c.topic_id, c.date, f.id
FROM ".$prefix."comment AS c LEFT JOIN ".$prefix."forum AS f ON f.id = c.topic_id GROUP BY f.id ORDER BY date DESC LIMIT 3");
$newer_one = mysql_fetch_array($new_forum_query);
//get all the comments
$comment_query = mysql_query("SELECT id, topic_id, deck_id, date FROM ".$prefix."comment ORDER BY date DESC LIMIT 3");
while ($comment = mysql_fetch_assoc($comment_query))
{
if($comment['topic_id']==0)
{
$deck_query = mysql_query("SELECT * FROM ".$prefix."decks WHERE id=".$comment['deck_id']);
while ($deck_comments = mysql_fetch_assoc($deck_query))
{
//print all the things!
}
}
elseif($comment['deck_id']==0)
{
$forum_query = mysql_query("SELECT * FROM ".$prefix."forum WHERE id=".$comment['topic_id']);
while ($forum_comments = mysql_fetch_assoc($forum_query))
{
//print all the things!
}
}
}

How can I combine 2 complex mysql queries

So I have two MySQL queries that if I had the knowledge to combine I would but I don't so that's why I turned to "SO", and in that case I haven't tried anything because its out of my scope. I want to combine all in one query and if that's not possible please let me know.
Query one "This selects all of your friends posts including yours":
"SELECT b.*, c.photo, d.name, e.status
FROM post b
INNER JOIN profile c
INNER JOIN user d
INNER JOIN user_friendship e
ON b.from_user = c.user_id
AND b.from_user = d.id
AND e.friend_id = b.from_user
WHERE e.status = :status
AND e.user_id = :id
ORDER BY b.id DESC LIMIT 20"
Query two "This selects all of the people your following posts":
"SELECT b.*, c.photo, d.name, e.status
FROM post b
INNER JOIN profile c
INNER JOIN user d
INNER JOIN user_follower e
ON b.from_user = c.user_id
AND b.from_user = d.id
AND e.to_id = b.from_user
WHERE e.status = :status
AND e.who_id = :id
ORDER BY b.id DESC LIMIT 20"
I have combined these but with php alone. I'd like to combine both in one single MySQL query. Thanks in advance
SELECT *
FROM
(SELECT b.*,
c.photo,
d.name,
e.status
FROM post b
INNER JOIN profile c
INNER JOIN USER d
INNER JOIN user_friendship e ON b.from_user = c.user_id
AND b.from_user = d.id
AND e.friend_id = b.from_user
WHERE e.status = :status
AND e.user_id = :id LIMIT 20
UNION SELECT b.*,
c.photo,
d.name,
e.status
FROM post b
INNER JOIN profile c
INNER JOIN USER d
INNER JOIN user_follower e ON b.from_user = c.user_id
AND b.from_user = d.id
AND e.to_id = b.from_user
WHERE e.status = :status
AND e.who_id = :id LIMIT 20 ) MainQuery
ORDER BY id DESC
SELECT b.*, c.photo, d.name, e.status, "P"
FROM post b
INNER JOIN profile c
INNER JOIN user d
INNER JOIN user_friendship e
ON b.from_user = c.user_id
AND b.from_user = d.id
AND e.friend_id = b.from_user
WHERE e.status = :status
AND e.user_id = :id
ORDER BY b.id DESC LIMIT 20
UNION
SELECT b.*, c.photo, d.name, e.status, "F"
FROM post b
INNER JOIN profile c
INNER JOIN user d
INNER JOIN user_follower e
ON b.from_user = c.user_id
AND b.from_user = d.id
AND e.to_id = b.from_user
WHERE e.status = :status
AND e.who_id = :id
ORDER BY b.id DESC LIMIT 20
With the extra column you can see where the row is coming from (P = post, F = following)
Keep in mind that union will remove duplicate rows. If you want to see all rows use UNION ALL

optimizing the 3 queries into one

I am just trying to figure out on how to optimize the below sequence of queries into a single query.
To be specific, below queries are just like alerts to be displayed to user when he logins into his account.
$sq = "SELECT COUNT(*) as totm FROM login as l
JOIN msgs m on m.id = l.id
WHERE m.tstamp > l.mtstamp AND l.id = $id;";
$sq .= "SELECT COUNT(*) as totp FROM info as u
JOIN pst as p on p.cid = u.cid
JOIN login as l on l.id = u.id
WHERE p.tstamp > l.ptstamp AND p.id <> u.id
AND p.type = 0 AND u.id = $id;";
$sq .= "SELECT COUNT(*) as totq FROM info as u
JOIN pst as p on p.cid = u.cid
JOIN login as l on l.id = u.id
WHERE p.tstamp > l.ptstamp AND p.id <> u.id
AND p.type = 1 AND u.id = $id";
Right now Iam using mysqli_multi_query() to run this multiple queries.
However I have managed to cut it down into a single query
$sq = "SELECT m.totm,p.totp FROM login as l
JOIN info as u on u.id = l.id
LEFT JOIN (SELECT tstamp,id,COUNT(*) as 'totm' FROM msgs GROUP BY id) m
ON m.id = l.id AND m.tstamp > l.mtstamp
LEFT JOIN (SELECT tstamp,cid,type,id,COUNT(*) as 'totp' FROM pst
GROUP BY id) p
ON p.cid = u.cid AND p.tstamp > l.ptstamp AND p.type = 0
AND p.id <> l.id WHERE l.id = $id
LEFT JOIN (SELECT tstamp,cid,type,id,COUNT(*) as 'totp' FROM pst
GROUP BY id) p
ON p.cid = u.cid AND p.tstamp > l.ptstamp AND p.type = 1
AND p.id <> l.id WHERE l.id = $id LIMIT 1";
When I have tried the single query with EXPLAIN STATEMENT the query ouput is too bad, many rows are affected.
When I have performed the same on three queries individually, the result was good. I am not sure should I run multiple queries or try single query.
After googling I have found that groupBY causes severe overhead in joins and for many rows. kindly anyone let me know what would be the better way to approach this. Can anyone me help me for writing a more optimized query. Thank you. Any help is greatly appreciated.
If you're trying to retrieve the count for a specific id, then I recommend combining your queries as subqueries in the select. There's no sense in using GROUP BY since you're looking for the count of a specific id.
SELECT
(SELECT COUNT(*) FROM login as l
JOIN msgs m on m.id = l.id
WHERE m.tstamp > l.mtstamp AND l.id = $id) totm,
(SELECT COUNT(*) FROM info as u
JOIN pst as p on p.cid = u.cid
JOIN login as l on l.id = u.id
WHERE p.tstamp > l.ptstamp AND p.id <> u.id
AND p.type = 0 AND u.id = $id) totp,
(SELECT COUNT(*) FROM info as u
JOIN pst as p on p.cid = u.cid
JOIN login as l on l.id = u.id
WHERE p.tstamp > l.ptstamp AND p.id <> u.id
AND p.type = 1 AND u.id = $id) totq
Contrast this to retrieving counts for every id in which case a GROUP BY on id would be useful:
SELECT t1.id,t1.totm,t2.totp,t3.totq
FROM
(SELECT l.id, COUNT(*) as totm FROM login as l
JOIN msgs m on m.id = l.id
WHERE m.tstamp > l.mtstamp
GROUP BY l.id) t1
LEFT JOIN (SELECT u.id, COUNT(*) as totp FROM info as u
JOIN pst as p on p.cid = u.cid
JOIN login as l on l.id = u.id
WHERE p.tstamp > l.ptstamp AND p.id <> u.id
AND p.type = 0
GROUP BY u.id) t2 ON t1.id = t2.id
LEFT JOIN (SELECT u.id, COUNT(*) as totq FROM info as u
JOIN pst as p on p.cid = u.cid
JOIN login as l on l.id = u.id
WHERE p.tstamp > l.ptstamp AND p.id <> u.id
AND p.type = 1
GROUP BY u.id) t3 ON t2.id = t3.id

SQL: Delete from multiple tables using Joins

The following will work if each of the three tables yields results, however if just one of these tables chucks nothing, then it breaks the query and doesn't delete anything. Is there a way to bulletproof this query so if just 1 of the three tables has results, they will be deleted?
DELETE e, f, c
FROM `users exercises` e
inner join `users foods` f on f.userid = e.userid and f.`date` = e.`date`
inner join `users check-ins` c on c.userid = e.userid and c.`date` = e.`date`
WHERE e.`date` = '$date' AND e.userid = '$user->id'
Do you have a 'users' table? If so, then you can write something like this:
DELETE e, f, c
FROM 'users' u
left join 'users exercises' e on e.userid = u.userid and e.`date` = '$date'
left join 'users foods' f on f.userid = u.userid and f.`date` = '$date'
left join 'users check-ins' c on c.userid = u.userid and c.`date` = '$date'
WHERE u.userid = '$user->id'

Yii CDbCommandBuilder query

I'm trying to perform a query in Yii with CDbCommandBuilder so I can have the resultset in an array.
Problem is that I don't understand how to convert my SQL (pgsql) to the Yii CDbCommandBuilder syntax. Mainly my problem is with nested joins.
The query:
SELECT p.id
up.id as fid,
sum(CASE
WHEN v1.count>v2.count THEN v2.count
ELSE v1.count
END
) as res
FROM product as v1
INNER JOIN (
SELECT p_id, count
FROM product
WHERE user_id = {$user_id}) as v2 on v1.p_id = v2.p_id and v1.user_id <> {$user_id}
RIGHT JOIN users as p on p.id = v1.user_id
INNER JOIN uf on uf.friend_id = p.id and uf.user_id = {$user_id} and is_active = true
INNER JOIN up on up.user_id = p.id and is_active = true
GROUP BY p.id, up.id
ORDER BY res desc
Can anyone help?
Thanks
$sql = "SELECT p.id
up.id as fid,
sum(CASE
WHEN v1.count>v2.count THEN v2.count
ELSE v1.count
END
) as res
FROM product as v1
INNER JOIN (
SELECT p_id, count
FROM product
WHERE user_id = :user_id) as v2 on v1.p_id = v2.p_id and v1.user_id <> :user_id
RIGHT JOIN users as p on p.id = v1.user_id
INNER JOIN uf on uf.friend_id = p.id and uf.user_id = :user_id and is_active = true
INNER JOIN up on up.user_id = p.id and is_active = true
GROUP BY p.id, up.id
ORDER BY res desc";
$params = array(':user_id' => $user_id);
$aArrayOfRows = Yii::app()->db->createCommand($sql)->queryAll(true, $params);

Categories