I have two straightforward SELECT queries left-joining the same table in a MySQL DB:
SELECT uID, externaluID, COUNT(editID) AS editCount
FROM users
LEFT JOIN (edits
INNER JOIN posts
ON postRefID = postID AND editAuthorID <> authorID AND isa = 0)
ON editAuthorID = uID
GROUP BY uID
ORDER BY uID;
SELECT uID, externaluID, COUNT(posts.postID) AS postCount, SUM(value)
FROM users
LEFT JOIN (posts
LEFT JOIN usrR ON posts.postID = usrR.postID)
ON authorID = uID
GROUP BY authorID
ORDER BY uID;
So far so good. Now I want to merge these queries. My approach was
SELECT uID, externaluID, COUNT(editID) AS editCount, COUNT(P2.postID) AS postCount, SUM(rateValue)
FROM users
LEFT JOIN (edits
INNER JOIN posts AS P1
ON postRefID = P1.postID AND editAuthorID <> P1.authorID AND isa = 0)
ON editAuthorID = uID
LEFT JOIN (posts AS P2
LEFT JOIN usrR ON P2.postID = usrR.postID)
ON P2.authorID = uID
GROUP BY P2.authorID, uID
ORDER BY uID;
but it returns wrong results. What am I doing wrong?
Try this one:
SELECT u1.uID, u1.externaluID, COUNT(e1.editID) AS editCount, u2.postCount, u2.sumValue
FROM users u1
LEFT JOIN (edits INNER JOIN posts
ON postRefID = postID AND editAuthorID <> authorID AND isa = 0) as e1
ON e1.editAuthorID = u1.uID
LEFT JOIN (
SELECT uID, externaluID, COUNT(posts.postID) AS postCount, SUM(value) as sumValue
FROM users
LEFT JOIN (posts
LEFT JOIN usrR ON posts.postID = usrR.postID)
ON authorID = uID
GROUP BY authorID
ORDER BY uID) as u2 on u1.uID = u2.uID
GROUP BY u1.uID,u2.postCount, u2.sumValue
ORDER BY u1.uID
Related
I don't know much about query optimization but I know the order in which queries get executed
FROM clause
WHERE clause
GROUP BY clause
HAVING clause
SELECT clause
ORDER BY clause
This the query I had written
SELECT
`main_table`.forum_id,
my_topics.topic_id,
(
SELECT MAX(my_posts.post_id) FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id
) AS `maxpostid`,
(
SELECT my_posts.admin_user_id FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id ORDER BY my_posts.post_id DESC LIMIT 1
) AS `admin_user_id`,
(
SELECT my_posts.user_id FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id ORDER BY my_posts.post_id DESC LIMIT 1
) AS `user_id`,
(
SELECT COUNT(my_topics.topic_id) FROM my_topics WHERE my_topics.forum_id = main_table.forum_id ORDER BY my_topics.forum_id DESC LIMIT 1
) AS `topicscount`,
(
SELECT COUNT(my_posts.post_id) FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id ORDER BY my_topics.topic_id DESC LIMIT 1
) AS `postcount`,
(
SELECT CONCAT(admin_user.firstname,' ',admin_user.lastname) FROM admin_user INNER JOIN my_posts ON my_posts.admin_user_id = admin_user.user_id WHERE my_posts.post_id = maxpostid ORDER BY my_posts.post_id DESC LIMIT 1
) AS `adminname`,
(
SELECT forum_user.nick_name FROM forum_user INNER JOIN my_posts ON my_posts.user_id = forum_user.user_id WHERE my_posts.post_id = maxpostid ORDER BY my_posts.post_id DESC LIMIT 1
) AS `nickname`,
(
SELECT CONCAT(ce1.value,' ',ce2.value) AS fullname FROM my_posts INNER JOIN customer_entity_varchar AS ce1 ON ce1.entity_id = my_posts.user_id INNER JOIN customer_entity_varchar AS ce2 ON ce2.entity_id=my_posts.user_id WHERE (ce1.attribute_id = 1) AND (ce2.attribute_id = 2) AND my_posts.post_id = maxpostid ORDER BY my_posts.post_id DESC LIMIT 1
) AS `fullname`
FROM `my_forums` AS `main_table`
LEFT JOIN `my_topics` ON main_table.forum_id = my_topics.forum_id
WHERE (forum_status = '1')
And now I want to know if there is any way to optimize it ? Because all the logic is written in Select section not From, but I don't know how to write the same logic in From section of the query ?
Does it make any difference or both are same ?
Thanks
Correlated subqueries should really be a last resort, they often end up being executed RBAR, and given that a number of your subqueries are very similar, trying to get the same result using joins is going to result in a lot less table scans.
The first thing I note is that all of your subqueries include the table my_posts, and most contain ORDER BY my_posts.post_id DESC LIMIT 1, those that don't have a count with no group by so the order and limit are redundant anyway, so my first step would be to join to my_posts:
SELECT *
FROM my_forums AS f
LEFT JOIN my_topics AS t
ON f.forum_id = t.forum_id
LEFT JOIN
( SELECT topic_id, MAX(post_id) AS post_id
FROM my_posts
GROUP BY topic_id
) AS Maxp
ON Maxp.topic_id = t.topic_id
LEFT JOIN my_posts AS p
ON p.post_id = Maxp.post_id
WHERE forum_status = '1';
Here the subquery just ensures you get the latest post per topic_id. I have shortened your table aliases here for my convenience, I am not sure why you would use a table alias that is longer than the actual table name?
Now you have the bulk of your query you can start adding in your columns, in order to get the post count, I have added a count to the subquery Maxp, I have also had to add a few more joins to get some of the detail out, such as names:
SELECT f.forum_id,
t.topic_id,
p.post_id AS `maxpostid`,
p.admin_user_id,
p.user_id,
t2.topicscount,
maxp.postcount,
CONCAT(au.firstname,' ',au.lastname) AS adminname,
fu.nick_name AS nickname
CONCAT(ce1.value,' ',ce2.value) AS fullname
FROM my_forums AS f
LEFT JOIN my_topics AS t
ON f.forum_id = t.forum_id
LEFT JOIN
( SELECT topic_id,
MAX(post_id) AS post_id,
COUNT(*) AS postcount
FROM my_posts
GROUP BY topic_id
) AS Maxp
ON Maxp.topic_id = t.topic_id
LEFT JOIN my_posts AS p
ON p.post_id = Maxp.post_id
LEFT JOIN admin_user AS au
ON au.admin_user_id = p.admin_user_id
LEFT JOIN forum_user AS fu
ON fu.user_id = p.user_id
LEFT JOIN customer_entity_varchar AS ce1
ON ce1.entity_id = p.user_id
AND ce1.attribute_id = 1
LEFT JOIN customer_entity_varchar AS ce2
ON ce2.entity_id = p.user_id
AND ce2.attribute_id = 2
LEFT JOIN
( SELECT forum_id, COUNT(*) AS topicscount
FROM my_topics
GROUP BY forum_id
) AS t2
ON t2.forum_id = f.forum_id
WHERE forum_status = '1';
I am not familiar with your schema so the above may need some tweaking, but the principal remains - use JOINs over sub-selects.
The next stage of optimisation I would do is to get rid of your customer_entity_varchar table, or at least stop using it to store things as basic as first name and last name. The Entity-Attribute-Value model is an SQL antipattern, if you added two columns, FirstName and LastName to your forum_user table you would immediately lose two joins from your query. I won't get too involved in the EAV vs Relational debate as this has been extensively discussed a number of times, and I have nothing more to add.
The final stage would be to add appropriate indexes, you are in the best decision to decide what is appropriate, I'd suggest you probably want indexes on at least the foreign keys in each table, possibly more.
EDIT
To get one row per forum_id you would need to use the following:
SELECT f.forum_id,
t.topic_id,
p.post_id AS `maxpostid`,
p.admin_user_id,
p.user_id,
MaxT.topicscount,
maxp.postcount,
CONCAT(au.firstname,' ',au.lastname) AS adminname,
fu.nick_name AS nickname
CONCAT(ce1.value,' ',ce2.value) AS fullname
FROM my_forums AS f
LEFT JOIN
( SELECT t.forum_id,
COUNT(DISTINCT t.topic_id) AS topicscount,
COUNT(*) AS postCount,
MAX(t.topic_ID) AS topic_id
FROM my_topics AS t
INNER JOIN my_posts AS p
ON p.topic_id = p.topic_id
GROUP BY t.forum_id
) AS MaxT
ON MaxT.forum_id = f.forum_id
LEFT JOIN my_topics AS t
ON t.topic_ID = Maxt.topic_ID
LEFT JOIN
( SELECT topic_id, MAX(post_id) AS post_id
FROM my_posts
GROUP BY topic_id
) AS Maxp
ON Maxp.topic_id = t.topic_id
LEFT JOIN my_posts AS p
ON p.post_id = Maxp.post_id
LEFT JOIN admin_user AS au
ON au.admin_user_id = p.admin_user_id
LEFT JOIN forum_user AS fu
ON fu.user_id = p.user_id
LEFT JOIN customer_entity_varchar AS ce1
ON ce1.entity_id = p.user_id
AND ce1.attribute_id = 1
LEFT JOIN customer_entity_varchar AS ce2
ON ce2.entity_id = p.user_id
AND ce2.attribute_id = 2
WHERE forum_status = '1';
I'm looking for a way to excluding some records before i the left join executes.
My sql statement looks as follows:
SELECT * FROM users
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = users.id
WHERE p.id IS NULL
How can I exclude the records with the id 1 and two in the user table?
You could add criteria to your WHERE clause:
SELECT *
FROM users
LEFT JOIN ( SELECT *
FROM premissions
WHERE post_id = 1
) AS p
ON p.user_id = users.id
WHERE p.id IS NULL
AND users.id NOT IN (1,2)
Use a subselect
SELECT * FROM
(select * from users where id not in(1,2))
u
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = u.id
WHERE p.id IS NULL
SELECT * FROM
(select from users where id not in (1,2)) u
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = u.id
WHERE p.id IS NULL
you can try to filter in where clause or do it as M Khalid Junaid has mentioned.
SELECT * FROM users
LEFT JOIN (SELECT * FROM premissions WHERE post_id = 1) AS p
ON p.user_id = users.id
WHERE p.id IS NULL
and users.id not in (1, 2)
I have this query as my main query, I use all the records from the members table, and selected columns from comments and chat_box.
SELECT members.*,
a.commenter_id,
b.user_id,
a.comcount,
b.chatcount
FROM members
LEFT JOIN (SELECT commenter_id,
Count(*) comCount
FROM comments
GROUP BY commenter_id) a
ON members.id = a.commenter_id
LEFT JOIN (SELECT user_id,
Count(*) chatCount
FROM chat_box
GROUP BY user_id) b
ON members.id = b.user_id
WHERE members.id = '290'
I would like to add this query to the above
SELECT Count(friend_id) AS totFriend,
friend_id AS fi,
logged_user_id AS user,
friend_accepted AS fa
FROM member_friends
WHERE logged_user_id = '1'
AND friend_id = '290'
Is it possible to add this to the mix without causing any errors? I have tried it myself but I maybe just putting my self in deeper problems. If it is possible could someone assist me in doing so, thank you :).
If you have a way to join the last query to your members table, then you should be able to use:
SELECT members.*,
a.commenter_id,
b.user_id,
a.comcount,
b.chatcount,
c.totFriend
FROM members
LEFT JOIN
(
SELECT commenter_id,
Count(*) comCount
FROM comments
GROUP BY commenter_id
) a
ON members.id = a.commenter_id
LEFT JOIN
(
SELECT user_id,
Count(*) chatCount
FROM chat_box
GROUP BY user_id
) b
ON members.id = b.user_id
LEFT JOIN
(
SELECT Count(friend_id) AS totFriend,
friend_id AS fi,
logged_user_id AS user,
friend_accepted AS fa
FROM member_friends
WHERE logged_user_id = '1'
AND friend_id = '290'
) c
on members.id = c.friend_id
WHERE members.id = '290'
I have the following query, in which I used JOINs. It says:
unknown column m.bv ..
Could you please take a look and tell me what I'm doing wrong?
$query4 = 'SELECT u.*, SUM(c.ts) AS total_sum1, SUM(m.bv) AS total_sum
FROM users u
LEFT JOIN
(SELECT user_id ,SUM(points) AS ts FROM coupon GROUP BY user_id) c
ON u.user_id=c.user_id
LEFT JOIN
(SELECT user_id ,SUM(points) AS bv FROM matching GROUP BY user_id) r
ON u.user_id=m.user_id
where u.user_id="'.$_SESSION['user_name'].'"
GROUP BY u.user_id';
You are selecting SUM(points) AS bv from the table with the alias r, there is no tables with the alias m. So that it has to be r.bv instead like so:
SELECT
u.*,
SUM(c.ts) AS total_sum1,
SUM(r.bv) AS total_sum
FROM users u
LEFT JOIN
(
SELECT
user_id,
SUM(points) AS ts
FROM coupon
GROUP BY user_id
) c ON u.user_id=c.user_id
LEFT JOIN
(
SELECT
user_id,
SUM(points) AS bv
FROM matching
GROUP BY user_id
) r ON u.user_id = m.user_id
where u.user_id="'.$_SESSION['user_name'].'"
GROUP BY u.user_id
Replace m., with r. Look at second Join
You have aliased the derived table with r and you reference that table (twice) with m. Correct one or the other.
Since you group by user_id in the two subqueries and user_id is (I assume) the primary key of table user, you don't really need the final GROUP BY.
I would write it like this, if it was meant for all (many) users:
SELECT u.*, COALESCE(c.ts, 0) AS total_sum1, COALESCE(m.bv, 0) AS total_sum
FROM users u
LEFT JOIN
(SELECT user_id, SUM(points) AS ts FROM coupon GROUP BY user_id) c
ON u.user_id = c.user_id
LEFT JOIN
(SELECT user_id, SUM(points) AS bv FROM matching GROUP BY user_id) m
ON u.user_id = m.user_id
and like this in your (one user) case:
SELECT u.*, COALESCE(c.ts, 0) AS total_sum1, COALESCE(m.bv, 0) AS total_sum
FROM users u
LEFT JOIN
(SELECT SUM(points) AS ts FROM coupon
WHERE user_id = "'.$_SESSION['user_name'].'") c
ON TRUE
LEFT JOIN
(SELECT SUM(points) AS bv FROM matching
WHERE user_id = "'.$_SESSION['user_name'].'") m
ON TRUE
WHERE u.user_id = "'.$_SESSION['user_name'].'"
The last query can also be simplified to:
SELECT u.*,
COALESCE( (SELECT SUM(points) FROM coupon
WHERE user_id = u.user_id)
, 0) AS total_sum1,
COALESCE( (SELECT SUM(points) FROM matching
WHERE user_id = u.user_id)
, 0) AS total_sum
FROM users u
WHERE u.user_id = "'.$_SESSION['user_name'].'"
My query is
select COUNT(*) from result
where test_id in (select test_id
from test_schedule
where scheduler_id in (select user_id
from users
where user_type=1))
Try this:
SELECT COUNT(r.*)
FROM result r
INNER JOIN test_schedule s ON r.test_id = s.test_id
INNER JOIN users u ON s.scheduler_id = u.user_id
WHERE u.user_type = 1
SELECT COUNT(*)
FROM result r
JOIN (SELECT DISTINCT test_id
FROM test_schedule s
JOIN users u ON s.scheduler_id = u.user_id
WHERE u.user_type = 1) s
USING (test_id)
The DISTINCT is necessary to keep rows from being multiplied by all the rows in the other tables that match.
SELECT COUNT(r.*)
FROM result r
RIGHT JOIN test_schedule s USING(test_id)
RIGHT JOIN users u ON s.scheduler_id = u.user_id
WHERE u.user_type = 1