mysql multiple fields from subquery - mysql

SELECT
(SELECT date FROM forums WHERE topic_id=f.id OR id=f.id ORDER BY id DESC LIMIT 1) as last_reply,
f.*, p.id as pid, p.name FROM forums f
INNER JOIN players p ON p.id = f.author
WHERE f.topic_id=0 ORDER BY f.id DESC
In the subquery, I'd like to return not only the date field, but also the author field as well. how can I do this?
looked at a similar post but can't apply it to mine.

I would do it something like this:
SELECT
(SELECT date FROM forums WHERE topic_id=f.id OR id=f.id ORDER BY id DESC LIMIT 1) as last_reply,
(SELECT author FROM forums WHERE topic_id=f.id OR id=f.id ORDER BY id DESC LIMIT 1) as last_author,
f.*, p.id as pid, p.name FROM forums f
INNER JOIN players p ON p.id = f.author
WHERE f.topic_id=0 ORDER BY f.id DESC
I would actually repeat the subquery again

Maybe something like this can help you :
SELECT MAX(f.date), f.author
FROM forums f
INNER JOIN players p ON
p.id = f.author
WHERE f.tpoic_id = 0
GROUP BY f.author
ORDER BY f.id DESC
But it's difficult with no structure of tables.
Good luck.

Related

SQL: how to include records that dont exist

I am making a query where I sort posts by upvotes. I have table posts and votes(post_id, user_id, vote), where vote can be 1 or -1. So now my problem is if post does not have any upvotes it won't show in result at all.
My query:
SELECT P.* FROM `vicoteka-api`.posts P
INNER JOIN (
SELECT post_id, COUNT(*) vote_count FROM `vicoteka-api`.votes
WHERE vote = 1 GROUP BY post_id
) V ON P.id = V.post_id
ORDER BY V.vote_count DESC
How can I include posts that don't exist in votes pivot table?
Use a left join with coalesce to get posts with no votes.
SELECT P.*,COALESCE(v.vote_count,0) as vote_count
FROM `vicoteka-api`.posts P
LEFT JOIN (SELECT post_id, COUNT(*) vote_count
FROM `vicoteka-api`.votes
WHERE vote = 1
GROUP BY post_id
) V ON P.id = V.post_id
ORDER BY vote_count DESC
Vamsi's answer is correct. If performance is a consideration, you might want to compare it to:
SELECT P.*, COUNT(v.post_id) as vote_count
FROM vicoteka-api.posts P LEFT JOIN
vicoteka-api.votes v
ON P.id = V.post_id AND v.vote = 1
GROUP BY p.id
ORDER BY vote_count DESC;
Or, what might even be better:
SELECT p.*,
(SELECT COUNT(*)
FROM vicoteka-api.votes v
WHERE P.id = V.post_id AND v.vote = 1
) as vote_count
FROM vicoteka-api.posts p
ORDER BY vote_count DESC;
The latter two allow an index to be used for the JOIN. The last one even saves effort on the GROUP BY.
SELECT P.*, V.POST_ID, V_COUNT, VOTE_COUNT FROM `vicoteka-api`.posts P
RIGHT OUTER JOIN (
SELECT post_id, COUNT( NVL(vote_count),0) AS V_COUNT, VOTE_COUNT
FROM `vicoteka-api`.votes
GROUP BY post_id, vote_count
) V ON P.id = V.post_id and V.vote_count <> -1
ORDER BY V.vote_count DESC
Correct me if I am wrong.

MySQL: Sorting results before group by statement

Basically, I have a coppermine gallery and I want to show the last 4 updated albums on the homepage. Here's the query that I've got so far. It basically gets the latest pictures. The subquery works fine on it's own but when it comes time to grouping them to get each album on its own, it doesn't seem to be getting the most recent one from the list.
SELECT *
FROM (
SELECT c.cid, c.name AS catname, a.aid, a.title AS albumtitle, a.category, p.aid AS albumid,p.filepath,p.filename,p.ctime AS creationtime,p.title AS pictitle,p.approved
FROM cpg145_pictures AS p LEFT JOIN `cpg145_albums` AS a ON p.aid = a.aid LEFT JOIN `cpg145_categories` AS c ON a.category = c.cid
WHERE p.approved='YES' AND a.category IN (47,48)
ORDER BY p.ctime DESC) AS T
GROUP BY albumid
ORDER BY creationtime DESC
LIMIT 4
I figured out the answer. Apparently, in MariaDB you have to give the subquery a limit for it to be sorted correctly. So:
SELECT *
FROM (
SELECT c.cid, c.name AS catname, a.aid, a.title AS albumtitle, a.category, p.aid AS albumid,p.filepath,p.filename,p.ctime AS creationtime,p.title AS pictitle,p.approved
FROM cpg145_pictures AS p LEFT JOIN `cpg145_albums` AS a ON p.aid = a.aid LEFT JOIN `cpg145_categories` AS c ON a.category = c.cid
WHERE p.approved='YES' AND a.category IN (47,48)
ORDER BY p.ctime DESC
LIMIT 200) AS T
GROUP BY albumid
ORDER BY creationtime DESC
LIMIT 4

MySQL subquery with LIMIT alternative

What I want:
SELECT u.username, u.last_activity
FROM users_userprofile
WHERE u.id IN (
SELECT DISTINCT(p.user_id) FROM forums_post p
WHERE p.thread_id = 423993
ORDER BY p.created_at DESC
LIMIT 4
);
This doesn't work because of LIMIT in subquery. I want to keep order of subquery but I want to get username and last_activity instead of user_id.
Any suggestion how I could achieve this?
Replace the subquery with a view:
CREATE VIEW subv AS SELECT p.user_id FROM forums_post p
WHERE p.thread_id = 423993
ORDER BY p.created_at DESC
LIMIT 4;
SELECT u.username, u.last_activity
FROM users_userprofile
WHERE u.id IN (SELECT * FROM subv);
you could use join for the table and the subquery instead of using where in:
SELECT u.username, u.last_activity
FROM users_userprofile u
JOIN (
SELECT p.user_id FROM forums_post p
WHERE p.thread_id = 423993
ORDER BY p.created_at DESC
LIMIT 4
) q
on u.user_id=q.user_id
Why woldn't you do it with a JOIN? There seems to be no performance impact because WHERE and LIMIT clauses are the same. It won't JOIN the whole tables:
SELECT p.user_id, u.username, u.last_activity
FROM users_userprofile u
JOIN forums_post p ON p.user_id = u.id
WHERE p.thread_id = 423993
GROUP BY p.user_id ORDER BY MAX(p.created_at) DESC
LIMIT 4

Select distinct to remove duplicate rows?

I have a forum with a Posts and Comments table. I'd like to sort by recent comments:
select distinct(p.id)
,p.title
,c.id
from Posts as p
,Comments as c
where c.post_id = p.id
order by c.id DESC
LIMIT 50;
However I get a row for every comment. I know I want to loop through the most recent comments and grab the first 50 unique posts. I just can't translate that to SQL.
Here's a solution without subqueries:
SELECT p.id, p.title, MAX(c.id) AS comment_id
FROM Posts AS p
JOIN Comments AS c
ON c.post_id = p.id
GROUP BY p.id
ORDER by comment_id DESC
LIMIT 50
This way may be a bit faster and more scalable despite the subquery because it can optimize on the limit clause:
SELECT p.id, p.title, MAX(c.id) AS comment_id
FROM Posts p
JOIN (SELECT DISTINCT c.post_id FROM Comments c ORDER BY c.id DESC LIMIT 50) t
ON t.post_id = p.id
JOIN Comments c
ON c.post_id = p.id
GROUP BY p.id
ORDER BY comment_id DESC
Make sure there is an index on Comments(post_id).
select p.id
,p.title
,c.id
from Posts as p
,Comments as c
where c.post_id in (
select distict (id)
from posts
)
order by c.id desc
limit 50;
Thanks, Gaurav
You can do so ,by getting the max of comment id for each post group and join with your posts table then do order by comments id
select p.id, p.title, c.id
from
Posts as p
JOIN
(select max(id) id ,post_id
from Comments group by
post_id LIMIT 50) c
ON(c.post_id = p.id)
order by c.id DESC;
Note above query will give the recent comment id only for each post group you can't use * in subquery to fetch the entire row for a comment this means this will not give the recent comment for each post if you select all in subquery
Edit this query will use only one join with limit in inner query so only 50 post's recent comment will be joined and its the inner join so it will take care to returned only associated posts,moreover the performance can be clear if you see the explain plan for your quesries

HQL/SQL select top 10 records based on count

I have 2 tables:
CATEGORY (id)
POSTING (id, categoryId)
I am trying to write an HQL or SQL query to find top 10 Categories which have the most number of Postings.
Help is appreciated.
SQL query:
SELECT c.Id, sub.POSTINGCOUNT
FROM CATEGORY c where c.Id IN
(
SELECT TOP 10 p.categoryId
FROM POSTING p
GROUP BY p.categoryId
order by count(1) desc
)
HQL:
Session.CreateQuery("select c.Id
FROM CATEGORY c where c.Id IN
(
SELECT p.categoryId
FROM POSTING p
GROUP BY p.categoryId
order by count(1) desc
)").SetMaxResults(10).List();
http://sqlinthewild.co.za/index.php/2010/01/12/in-vs-inner-join/
In SQL you can do this:
SELECT c.Id, sub.POSTINGCOUNT
FROM CATEGORY c
INNER JOIN
(
SELECT p.categoryId, COUNT(id) AS 'POSTINGCOUNT'
FROM POSTING p
GROUP BY p.categoryId
) sub ON c.Id = sub.categoryId
ORDER BY POSTINGCOUNT DESC
LIMIT 10
SQL can be like :
SELECT c.* from CATEGORY c, (SELECT count(id) as postings_count,categoryId
FROM POSTING
GROUP BY categoryId ORDER BY postings_count
LIMIT 10) d where c.id=d.categoryId
This output can be mapped to the Category entity.
I know that is an old question, but i reached a satisfatory answer.
JPQL:
//the join clause is necessary, because you cannot use p.category in group by clause directly
#NamedQuery(name="Category.topN",
query="select c, count(p.id) as uses
from Posting p
join p.category c
group by c order by uses desc ")
Java:
List<Object[]> list = getEntityManager().createNamedQuery("Category.topN", Object[].class)
.setMaxResults(10)
.getResultList();
//here we must made a conversion, because the JPA cannot order using a non select field (used stream API, but you can do it in old way)
List<Category> cats = list.stream().map(oa -> (Category) oa[0]).collect(Collectors.toList());