So I don't know much about MySQL but I heard about views and I'm trying to wrath my head around it.
Basically what I want to do is
check the table forum_posts, count the number of posts made by each user
query forum_users for each user to get every column and add to the view
query forum_threads to get the number of threads made by that user.
I don't know if that order is correct performance-wise but the final view should in theory look like either
1.
UId (user id from forum_users)
UName (user name from forum_users)
UThreads (user thread count)
UPosts (user post count)
UFakePosts (named UPosts in forum_users, I'll rename that later to UFakePosts)
ULastPost (this one is not that important but I'm just throwing it here in case anyone knows how to do it, I imagine it would be possible by selecting the post with the biggest PDate column)
2.
All of forum_users but renaming the forum_users.UPosts and forum_users.UThreads to UFakePosts and UFakeThreads
ULastPost
UThreads (user thread count)
UPosts (user post count)
I managed to get the post count working by using the following code
SELECT
IFNULL(a.UId,-1) AS UId,
IFNULL(a.UName,'Unknown') AS UName,
postsquery.Posts AS UPosts,
IFNULL(a.UPosts,-1) AS UFakePosts
FROM
(
SELECT p.PId, p.PAuthorId, COUNT(p1.PAuthorId) as Posts
FROM forum_posts AS p
LEFT JOIN forum_posts AS p1 ON p1.PId = p.PId
GROUP BY p.PAuthorId
)
AS postsquery
LEFT JOIN forum_users AS a ON postsquery.PAuthorId = a.UId
ORDER BY postsquery.Posts DESC
which generates the following result
but no success with getting threads, I can get one of the other but not both at the same time.
I've also tried this
SELECT IFNULL(a.UId,-1) AS UId,
IFNULL(a.UName,'Unknown') AS UName,
postsquery.Posts AS UPosts,
threadsquery.Threads AS UThreads,
IFNULL(a.UPosts,-1) AS UFakePosts
FROM
(
SELECT p.PId, p.PAuthorId, COUNT(p.PAuthorId) as Posts
FROM forum_posts AS p
)
AS postsquery
LEFT JOIN forum_users AS a1 ON postsquery.PAuthorId = a1.UId,
(
SELECT t.TId, t.TAuthorId, COUNT(t.TAuthorId) as Threads
FROM forum_threads AS t
GROUP BY t.TAuthorId
)
AS threadsquery
LEFT JOIN forum_users AS a ON threadsquery.TAuthorId = a.UId
ORDER BY
postsquery.Posts DESC
.....but the results are wrong:
What's supposed to happen:
Unknown (user that I haven't scraped yet): 1 post / 0 threads
User1: 2 posts / 0 threads
User2: 1 post / 2 threads
User3: 0 posts / 0 threads
If I could do another view but for threads, getting number of unique posters and number of posts that would be cool as well but one thing at a time.
Fiddle with database structure
http://sqlfiddle.com/#!9/c93d9/1
Structure should be pretty easy to understand, U stands for user, T for thread, P for post, D for date and so on.
Create a subselect to get the thread count:
LEFT JOIN
(
SELECT
TAuthorId,
COUNT(1) as thread_count
FROM
forum_threads
GROUP BY
TAuthorId
) threads ON
threads.TAuthorId = postsquery.PAuthorId
And then select that column:
IFNULL(threads.thread_count, 0) as thread_count
Putting it all together:
SELECT IFNULL(a.UId,-1) AS UId, IFNULL(a.UName,'Unknown') AS UName, IFNULL(postsquery.Posts, 0) AS UPosts, IFNULL(threads.thread_count, 0) as thread_count, IFNULL(a.UPosts,-1) AS UFakePosts
FROM
(
SELECT p.PId, p.PAuthorId, COUNT(p1.PAuthorId) as Posts
FROM forum_posts AS p
LEFT JOIN forum_posts AS p1 ON p1.PId = p.PId
GROUP BY p.PAuthorId
)
AS postsquery
LEFT JOIN forum_users AS a ON postsquery.PAuthorId = a.UId
LEFT JOIN
(
SELECT
TAuthorId,
COUNT(1) as thread_count
FROM
forum_threads
GROUP BY
TAuthorId
) threads ON
threads.TAuthorId = postsquery.PAuthorId
ORDER BY
postsquery.Posts DESC
Related
I have the following command:
SELECT *
FROM Posts P
WHERE P.ThreadId = 0
ORDER BY
(SELECT MAX(R.Time)
FROM Posts R
WHERE R.ThreadId = P.Id) DESC
This selects all threads and orders them by the time of their last reply. Threads without a reply are always behind threads with replies and in random order. I want this command to also order threads without replies by their creation time intermingled with the other threads.
How can I achieve this?
(Side note: Threads and Replies are using the same table "Post". A thread has the ThreadId 0 while a reply has the ThreadId of a parent post.)
When there is no element in the subquery fullfilling the condition (ie there is not reply to it) the subquery will return NULL thus you have to provide a default value. The function for that is COALESCE, which returns the first non-null value of its arguments.
SELECT *
FROM Posts P
WHERE P.ThreadId = 0
ORDER BY
COALESCE((SELECT MAX(R.Time)
FROM Posts R
WHERE R.ThreadId = P.Id), P.Time) DESC
So Coalesce( _thesubquery_, P.Time) will return the result of the subquery, if it's not null, or P.Time (ie the creation time of the non-answered post) otherwise.
You can also apply the COALESCE function just around the max instead of the whole subquery. This will lead to the same result
SELECT *
FROM Posts P
WHERE P.ThreadId = 0
ORDER BY
(SELECT COALESCE(MAX(R.Time), P.Time)
FROM Posts R
WHERE R.ThreadId = P.Id) DESC
SELECT p1.*,
COALESCE(p3.time, p1.time) last_thread_time
FROM posts p1
LEFT JOIN LATERAL ( SELECT MAX(p2.time) time
FROM posts p2
WHERE p2.thread_id = p1.id ) p3 ON TRUE
WHERE p1.thread_id = 0
ORDER BY COALESCE(p3.time, p1.time);
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=fc1a21abf4b4987a591c5e0536369142
I am trying to select the count of likes on a specific project. The idea i came up with is
CAST(count(uploads.ID in (SELECT uploadID from votes)) as decimal) as numberoflikes
this works but the query then only returns one thing.
Entire query
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,subjects.NAME AS subjectname
,uploads.TIME
,uploads.description
,uploads.NAME
,uploads.ID
,CASE
WHEN uploads.ID IN (
SELECT uploadID
FROM votes
WHERE userID = 2
)
THEN CAST(1 AS DECIMAL)
ELSE CAST(0 AS DECIMAL)
END AS liked
,CASE
WHEN uploads.ID IN (
SELECT uploadID
FROM bookmarks
WHERE userID = 2
)
THEN CAST(1 AS DECIMAL)
ELSE CAST(0 AS DECIMAL)
END AS bookmarked
,CAST(count(uploads.ID IN (
SELECT uploadID
FROM votes
)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN subjects ON (subjects.ID = uploads.subjectID)
INNER JOIN users ON (users.ID = uploads.userID)
INNER JOIN uploadGrades ON (uploads.ID = uploadGrades.uploadID)
INNER JOIN grades ON (grades.ID = uploadGrades.gradeID)
WHERE uploads.active = 1
AND subjects.ID IN (
SELECT subjectID
FROM userSubjects
INNER JOIN users ON (users.ID = userSubjects.userID)
WHERE userSubjects.userID = 2
)
AND grades.ID IN (
SELECT userGrades.gradeID
FROM uploadGrades
INNER JOIN userGrades ON (uploadGrades.gradeID = userGrades.gradeID)
WHERE userGrades.userID = 2
)
ORDER BY uploads.trueRating DESC;
Lets try a reduce version of your query, That is the base to get better answers
I reduce the initial query to user and upload to start. Also remove the fields you already know how to calculate.
.
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,uploads.NAME
,uploads.ID
,CAST(count(uploads.ID IN (
SELECT uploadID
FROM votes
)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN users ON (users.ID = uploads.userID)
WHERE uploads.active = 1
ORDER BY uploads.trueRating DESC;
Then add votes with LEFT JOIN to replace the SELECT in the COUNT that way if not match you will get NULL and as I say in my comment COUNT doesnt count NULL's
.
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,uploads.NAME
,uploads.ID
,CAST(count(votes.uploadID)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN users ON (users.ID = uploads.userID)
LEFT JOIN votes ON (uploads.ID = votes.uploadID)
WHERE uploads.active = 1
ORDER BY uploads.trueRating DESC;
Try something like this...
SELECT users.name as username, users.ID as userID, subjects.name as subjectname,
uploads.time, uploads.description, uploads.name, uploads.ID,
count(userVotes.userId), count(bookmarksMade.userId),
FROM uploads
join subjects on(subjects.ID = uploads.subjectID)
join users on(users.ID = uploads.userID)
join uploadGrades on(uploads.ID = uploadGrades.uploadID)
join grades on(grades.ID = uploadGrades.gradeID)
left join (select userId, uploadId from votes where userId = 2) as userVotes on uploads.id = userVotes.uploadId
left join (select userId, uploadId from bookmarks where userId = 2) as bookmarksMade on uploads.id = bookmarksMade.uploadId
join userSubjects on subjects.id = userSubjects.subjectID
WHERE uploads.active = 1 AND
userSubjects.userID = 2
ORDER BY uploads.trueRating DESC;
But, I am leaving out the userGrades thing, because you are doing a funky join there that I don't really understand (joining two tables on something that looks like it is not the whole primary key on either table).
Anyway, you really need to go to something more like this or what Oropeza suggests in his answer. Get more direct about what you want. This query looks like a monster that has been growing and getting things added in with "IN" clauses, as you needed them. Time to go back to the drawing board and think about what you want and how to get at it directly.
count(uploads.ID in (SELECT uploadID from votes)) as numberoflikes
group by uploads.Id ORDER BY uploads.trueRating DESC
I managed to do it like this. If i added the group by then it split the numberoflikes into rows and returned more then one row. Thanks for the help!
I have posts, swipes, notifications. I want to
sort posts by their score
delete swiped ones
join notifications with posts and put joined ones as top scores
So far I have done the first two but couldn't add the notifications as the first elements to the result. This is the working query of the first two.
SELECT posts.id, posts.created_at, posts.data, ((posts.data)->>'score')::NUMERIC as score
FROM posts
WHERE NOT EXISTS (
SELECT *
FROM swipes
WHERE ((swipes.data)->>'post_id')::INT = posts.id AND ((swipes.data)->>'user_id')::INT = 32)
ORDER BY (data)->>'score'
LIMIT 5
I tried LEFT JOIN for adding notifications but couldn't do it.
SELECT posts.id, posts.created_at, posts.data, ((posts.data)->>'score')::NUMERIC as score
FROM posts
WHERE NOT EXISTS (
SELECT *
FROM swipes
WHERE ((swipes.data)->>'post_id')::INT = posts.id AND ((swipes.data)->>'user_id')::INT = 32)
-- The part below is new
UNION ALL
SELECT notifications.id, notifications.created_at, notifications.data, 9999999 AS score
FROM notifications
--- THIS GIVES ERROR ---
LEFT JOIN posts USING (notifications.data)->>'post_id')
WHERE ((notifications.data)->>'user_id')::INT = 32
-- After join order by score
ORDER BY score
LIMIT 5
notifications has a column named data type json. notifications.data->post_id should joined by posts.id with it by score 9999999. where notifications.data->user_id should be equal to 32.
If you want the rows from table 'notifications' in addition to the rows from table 'posts', then you should UNION:
SELECT id, created_at, data, (data->>'score')::numeric AS score
FROM posts
WHERE NOT EXISTS (
SELECT 1
FROM swipes
WHERE ((swipes.data)->>'post_id')::int = posts.id
AND ((swipes.data)->>'user_id')::int = 32)
UNION ALL
SELECT id, created_at, data, 9999999 AS score
FROM notifications
LEFT JOIN posts USING (notifications.data)->>'post_id')
WHERE (data->>'user_id')::int = 32
-- After join order by score
ORDER BY score
LIMIT 5;
If instead you want to add the columns of table 'notifications' to those of table 'posts, then you should JOIN:
SELECT p.id, p.created_at, p.data, (p.data->>'score')::numeric AS score
n.id, n.created_at, n.data
FROM posts p
JOIN notifications n ON n->>'post_id' = p->>'post_id' -- guessing here
WHERE NOT EXISTS (
SELECT 1
FROM swipes
WHERE ((swipes.data)->>'post_id')::int = p.id
AND ((swipes.data)->>'user_id')::int = 32)
WHERE (n.data->>'user_id')::int = 32
ORDER BY score
LIMIT 5;
I have written an sql statement that besides all the other columns should return the number of comments and the number of likes of a certain post. It works perfectly when I don't try to get the number of times it has been shared too. When I try to get the number of time it was shared instead it returns a wrong number of like that seems to be either the number of shares and likes or something like that. Here is the code:
SELECT
[...],
count(CS.commentId) as shares,
count(CL.commentId) as numberOfLikes
FROM
(SELECT *
FROM accountSpecifics
WHERE institutionId= '{$keyword['id']}') `AS`
INNER JOIN
account A ON A.id = `AS`.accountId
INNER JOIN
comment C ON C.accountId = A.id
LEFT JOIN
commentLikes CL ON C.commentId = CL.commentId
LEFT JOIN
commentShares CS ON C.commentId = CS.commentId
GROUP BY
C.time
ORDER BY
year, month, hour, month
Could you also tell me if you think this is an efficient SQL statement or if you would do it differently? thank you!
Do this instead:
SELECT
[...],
(select count(*) from commentLikes CL where C.commentId = CL.commentId) as shares,
(select count(*) from commentShares CS where C.commentId = CS.commentId) as numberOfLikes
FROM
(SELECT *
FROM accountSpecifics
WHERE institutionId= '{$keyword['id']}') `AS`
INNER JOIN account A ON A.id = `AS`.accountId
INNER JOIN comment C ON C.accountId = A.id
GROUP BY C.time
ORDER BY year, month, hour, month
If you use JOINs, you're getting back one result set, and COUNT(any field) simply counts the rows and will always compute the same thing, and in this case the wrong thing. Subqueries are what you need here. Good luck!
EDIT: as posted below, count(distinct something) can also work, but it's making the database do more work than necessary for the answer you want to end up with.
Quick fix:
SELECT
[...],
count(DISTINCT CS.commentId) as shares,
count(DISTINCT CL.commentId) as numberOfLikes
Better approach:
SELECT [...]
, Coalesce(shares.numberOfShares, 0) As numberOfShares
, Coalesce(likes.numberOfLikes , 0) As numberOfLikes
FROM [...]
LEFT
JOIN (
SELECT commentId
, Count(*) As numberOfShares
FROM commentShares
GROUP
BY commentId
) As shares
ON shares.commentId = c.commentId
LEFT
JOIN (
SELECT commentId
, Count(*) As numberOfLikes
FROM commentLikes
GROUP
BY commentId
) As likes
ON likes.commentId = c.commentId
I have four different tables in my database:
thread:
thread_id
thread_content
timestamp
thread_rating:
thread_rating_id
thread_id
liked
disliked
thread_report:
thread_report_id
thread_id
thread_impression:
thread_impression_id
thread_id
And I'm going to join on these tables with this SQL-Query
SELECT t.thread_id,
t.thread_content,
SUM(tra.liked) AS liked,
SUM(tra.disliked) AS disliked,
t.timestamp,
((100*(tra.liked + SUM(tra.liked))) / (tra.liked + SUM(tra.liked) + (tra.disliked + SUM(tra.disliked)))) AS liked_percent,
((100*(COUNT(DISTINCT tre.thread_report_id)) / ((COUNT(DISTINCT ti.thread_impression_id))))) AS reported_percent
FROM thread AS t
LEFT JOIN thread_rating AS tra ON t.thread_id = tra.thread_id
LEFT JOIN thread_report AS tre ON tra.thread_id = tre.thread_id
LEFT JOIN thread_impression AS ti ON tre.thread_id = ti.thread_id
GROUP BY t.thread_id
ORDER BY liked_percent
The Query should return all thread_ids with the calculated liked and disliked, the likes in percent, the timestamp, when the thread was inserted into the database and the reports in percent to the impressions (the times, the thread was shown to the user)...
Nearly all results are right, the only results which are not right are the likes and dislikes.
If I put a count(*) in front of the query, I can see, that the right results have a count of 1 and the wrong ones have sometimes a count of up to 60.
Seems like there are cross join-problems...
I think that this is an issue with the Grouping, or perhaps I should embrace the Joins.
I've seen solutions with subselects. But I don't think that this is a great solutions for this issue...
What am I doing wrong here?
The tra table has multiple records per thread_id. This caused double counts in the SUM function.
Do the summations in a subselect, grouped by the join field.
That way you will only have one thread_id in tra2 to join with and duplicate rows will be avoided.
SELECT t.thread_id,
t.thread_content,
tra2.liked
tra2.disliked,
t.timestamp,
tra2.liked_percent,
((100*(COUNT(DISTINCT tre.thread_report_id)) / ((COUNT(DISTINCT ti.thread_impression_id))))) AS reported_percent
FROM thread AS t
LEFT JOIN (
SELECT
tra.thread_id
, SUM(tra.liked) AS liked
, SUM(tra.disliked) AS disliked
, ((100*(tra.liked + SUM(tra.liked))) / (tra.liked + SUM(tra.liked) + (tra.disliked + SUM(tra.disliked)))) AS liked_percent
FROM thread_rating AS tra
GROUP BY tra.thread_id
) as tra2 ON t.thread_id = tra2.thread_id
LEFT JOIN thread_report AS tre ON tra.thread_id = tre.thread_id
LEFT JOIN thread_impression AS ti ON tre.thread_id = ti.thread_id
GROUP BY t.thread_id
ORDER BY liked_percent DESC