How to select all this data without repeating subqueries? - mysql

Say I have a table called 'users', a table called 'posts' and a table called 'ratings', that stores the rating of each user to each post. If I wanted to select the posts, together with their thumbs up, thumbs down and 'average' ratings (rounded to the second decimal), I would do:
SELECT *,
(SELECT COUNT(*) FROM ratings WHERE thumb='up' AND post_id = posts.id) AS thumbs_up,
(SELECT COUNT(*) FROM ratings WHERE thumb='down' AND post_id = posts.id) AS thumbs_down,
ROUND((SELECT COUNT(*) FROM ratings WHERE thumb='up' AND post_id = posts.id) / (SELECT COUNT(*) FROM ratings WHERE post_id = posts.id), 2) AS average_rating
FROM posts;
But is there a way to obtain this same data without repeating the subqueries? I mean, ideally I would like to do:
SELECT *,
(SELECT COUNT(*) FROM ratings WHERE thumb='up' AND post_id = posts.id) AS thumbs_up,
(SELECT COUNT(*) FROM ratings WHERE thumb='down' AND post_id = posts.id) AS thumbs_down,
ROUND(thumbs_up / (thumbs_up + thumbs_down), 2) AS average_rating
FROM posts;
But MySQL does not allow this. What is next best thing? Or is there an even better way, by using JOINs or UNIONs?

SELECT
*,
ROUND(thumbs_up / (thumbs_up + thumbs_down), 2) AS average_rating
FROM (
SELECT
YourColumns...
SUM(CASE WHEN r.thumb = 'up' THEN 1 END) AS thumbs_up,
SUM(CASE WHEN r.thumb = 'down' THEN 1 END) AS thumbs_down
FROM
posts p LEFT JOIN
ratings r ON r.post_id = p.id
GROUP BY YoursColumns
) sub

Why not having in your "posts" table a column "ups" and "downs" where are summarized / counted the ups and downs ? you would end with something like this :
SELECT ups, downs, ROUND(ups / (ups + downs), 2) AS average_rating FROM posts where id = anId;

You could simply use variables inside a stored procedure. Code is concept and may need adjustment to fit mysql.
SET #thumbsup = 0;
SET #thumbsdown = 0;
SELECT #thumbsup = COUNT( * ) FROM ratings WHERE thumb = 'up' AND post_id = #someid;
SELECT #thumbsdown = COUNT( * ) FROM ratings WHERE thumb = 'down' AND post_id = #someid;
RETURN #thumbsup, #thumbsdown, ROUND( #thumbsup / ( thumbsup + thumbsdown ), 2 );

Related

How to select the max table from join table?

I have table name posts and another table which name is counttbl
In my post table there is column name
postid(primarykey), postdetails, postdate(timestamp)
And in counttbl there are 3 column which are
id(primarykey), postid,countnumber
I want to select that post from counttbl which has maximumnumber of count,
E.g. in post table I have
postid = 1, postdetails = details1, date = 29:11:00 00:00:00
And in count there is postid = 1, countnumber = 4, and postid = 2, countnumber = 3
Then I want to select that post which has maximumber count number and show that post details using join.
This statement would give you the desired result.
I named the tables and fields differen because of possible conflicts with reserved words in SQL.
SELECT * FROM
(
SELECT counts.postid, counts.counts
FROM counts
WHERE counts.counts = (SELECT max(counts) FROM counts)
) tempcounts
INNER JOIN posts ON posts.postid = tempcounts.postid
ORDER BY posts.postdate DESC limit 0,1
If more post have the same count they all will be in the result
Please use below query:
SELECT MAX(cnt.countnumber), cnt.postid
FROM counttbl as cnt
JOIN post as pst ON cnt.postid = pst.id
I have given table name:
Table name: post and counttbl
You can try below
select * from posts p
inner join
(
select postid, max(countnumber) from counttbl group by postid
) as p1
on p.postid=p1.postid
try this:
select * from post where postid in (select postid from counttbl having max(countnumber));
try this
select * from posts WHERE post_id IN (select MAX(countnumber) from counttble )

Selection of user's vote column based on ID

In my public voting database, I have pages and pages_votes tables with structure below:-
Page
Page Votes
i am querying mysql database to get results for all pages with their total sum of positive and negative votes
positive vote is 1, and negative vote is 0.
my query is as below:-
SELECT pages.id, pages.title, COUNT(pv.page_id) AS `total_count`, SUM(pv.vote=1) AS `likes`, SUM(pv.vote=0) AS `dislikes`
FROM `pages`
LEFT JOIN `pages_votes` AS `pv` ON `pages`.`id` = `pv`.`page_id`
GROUP BY `pages`.`id`, `pages`.`title`, `pages`.`slug`, `pages`.`image`
ORDER BY `total_count` DESC;
Results look like this (no issue here):-
Now I want to include a new custom column in this result called 'my_vote', my vote will show me my votes (user_id = 3) as 1 or 0 for like/dislikes if i have voted, and NULL if i have not voted.
There is already a user_id in page_votes table recording which user voted. How do I use that to get votes of a specific user with say ID = 10?
I suggest writing your query with table aliases. Then the answer to your question is a CASE expression:
SELECT p.id, p.title, COUNT(pv.page_id) AS total_count,
SUM(pv.vote =1 ) AS likes, SUM(pv.vote = 0) AS dislikes,
SUM(CASE WHEN pv.vote = 1 AND p.user_id = 3 THEN 1
WHEN pv.vote = 0 AND p.user_id = 3 THEN 0
END) as user_3_vote
FROM pages p LEFT JOIN
pages_votes pv
ON p.id = pv.page_id
GROUP BY p.id, p.title
ORDER BY total_count DESC;
Note that this uses SUM(CASE . . .) rather than SUM( <boolean expression> ). This is important so you can get a NULL value when there is no vote.
will you consider a small change here in your query? to get specified output.
SELECT pages.id, pages.title, COUNT(pv.page_id) AS `total_count`,
SUM(case when pv.vote = 1 then 1 else 0 end) AS `likes`,
SUM(case when pv.vote = 0 then 1 else 0 end) AS `dislikes`
FROM `pages`
LEFT JOIN `pages_votes` AS `pv`
ON `pages`.`id` = `pv`.`page_id`
GROUP BY `pages`.`id`, `pages`.`title`, `pages`.`slug`, `pages`.`image`
ORDER BY `total_count` DESC;

MySql throws error Subquery returns more than 1 row

I want to retrieve values from 3 table where i am getting error "Sub query returns more than 1 row " .
My concept is to retrieve all the post where i have to calculate the sum of votes from ttpostvotes table with respect to each post and if provided userid is voted for the that post then it will shows the post count like 1 or -1.
My query is as below:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes ,
(Select Votes From `ttpostvotes` where UserId=30 and x.PostId=r.PostId ) as IsUservoted,
(Select Count(*) From ttreply where PostId=r.PostId ) AS ReplyCount FROM `ttpost` r
left join ( SELECT PostId, sum(Votes) as Votes FROM `ttpostvotes` GROUP BY PostId ) x ON
x.PostId = r.PostId WHERE r.OffensiveCount<3 and r.SpamCount<5 and r.OtherCount<7 and r.`PeekId`=101 ORDER BY `r`.`PostTime` DESC
The 3 tables are like as below:
ttpost
ttpostvotes
ttreply
This is your select:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes,
(Select Votes From `ttpostvotes` where UserId = 30 and x.PostId = r.PostId
) as IsUservoted,
(Select Count(*) From ttreply where PostId=r.PostId ) AS ReplyCount
The first subquery has no aggregation, so I suppose a user could vote more than once for a post. This will fix the syntax error:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes,
(Select SUM(Votes) From `ttpostvotes` where UserId = 30 and x.PostId = r.PostId
) as IsUservoted,
(Select Count(*) From ttreply where PostId = r.PostId ) AS ReplyCount
Whether it does what you want is a different question.
Note: if you want your original query to work, you should define a unique constraint/index on ttpostvotes:
create unique index unq_ttpostvotes_userid_postid on ttpostvotes(userid, postid);

MySQL query joining 3 tables and counting

I've been stuck on this problem for far too long.
I have to merge 3 tables and do some counting of distinct values.
I have 3 tables
1.User_me
profileId( String )
responded( int 1 or 0)
2.Profiles
profileId ( String )
idLocation ( int )
3.lookup_location
id ( int )
location (String )
I can join User_me and Profiles ON User_me.profileId = Profiles.profileId
I can join Profiles and lookup_location ON Profiles.idLocation = lookup_location.id
Under Profiles I need to count the number of distinct values for idLocation where User_me.profileId = Profiles.profileId
I also need to count the number of Profiles.idLocation that have User_me.responded = 1
I have this:
SELECT lookup.location, count(*) as total
FROM User_me user
JOIN Profiles
ON user.profileId= profiles.profileId
JOIN lookup_location lookup
ON profiles.idLocation = lookup.id
GROUP BY profiles.idLocation
but I still need to have the column giving me the count where User_me.responded = 1
Something like:
SELECT lookup.location, count(*) as total, count(*) responded
If I'm understanding your question correctly, you can you a case statement in the count aggregate:
SELECT lookup.location, count(*) as total,
count(case when user.responded = 1 then 1 end) as responded
FROM User_me user
JOIN Profiles
ON user.profileId= profiles.profileId
JOIN lookup_location lookup
ON profiles.idLocation = lookup.id
GROUP BY profiles.idLocation
Since you're using MySQL, you can also use something like sum(user.responded = 1).

SQL Comparing 2 values that must be determined

I need to Find out the course with the most passes from my table tblResults.
tblResults:
StuID Course Symbol
1001 CSC101 P
1001 RNG101 F
1002 CSC101 P
1002 RNF101 F
1003 HAP101 P
1004 HAP101 P
i.e should give CSC101 (And all other courses (HAP101) with the same ammount of passes)
I have tried:
CREATE VIEW Part1 AS
SELECT NumbF
FROM
(SELECT COUNT(Course) AS NumbP,
Course
FROM tblResults
WHERE Symbol = 'P')
GROUP BY Course);
CREATE VIEW Part2 AS
SELECT MAX(NumbP) AS Maxnum
FROM Part1,
tblResults
WHERE Symbol = 'P'
GROUP BY Course;
SELECT Part1.Course
FROM Part1,
Part2
WHERE Part1.NumbP = Part2.MaxNum
But I seem to be doing something incorrectly. Please help
Something like this should work:
create view yourview as
select course, count(*) passcnt
from tblResults
where symbol = 'P'
group by course
select *
from yourview
where passcnt = (select max(passcnt) from yourview)
SQL Fiddle Demo
Note, you don't need the view, I just left it for simplicity.
SELECT Course, count(StuID) count
FROM tblResults
GROUP BY Course
HAVING count = (SELECT max(c)
FROM (SELECT count(StuID) c, Course
FROM tblResults
GROUP BY Course
WHERE Symbol = 'p'
) counts
);
You can try the SELECT below, but I haven't tested it:
SELECT selA.Course, selA.NumPass FROM (
SELECT Course, COUNT(Course) AS NumPass FROM
tblResults GROUP BY Course HAVING Symbol = 'P') AS selA
INNER JOIN (
SELECT MAX(NumPassCount) AS NumPassMax FROM (
SELECT COUNT(Course) AS NumPassCount FROM
tblResults GROUP BY Course HAVING Symbol = 'P') AS selB)
AS selC ON selA.NumPass = selC.NumPassMax
Using a couple of sub queries
SELECT Sub3.Course
FROM
(
SELECT MAX(Course) AS MaxCourseCnt
FROM
(
SELECT Course, COUNT(*) AS CourseCnt
FROM tblResults
WHERE Symbol = 'P'
GROUP BY Course
) Sub1
) Sub2
INNER JOIN
(
SELECT Course, COUNT(*) AS CourseCnt
FROM tblResults
WHERE Symbol = 'P'
GROUP BY Course
) Sub3
ON Sub2.MaxCourseCnt = Sub3.CourseCnt