Postgres JSON Join, Union - json

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;

Related

MySQL Group By with multi-join

here is my SQL query:
`SELECT subject, threadpost.date, threadpost.idThreadPost as id,
threadcategories.category, users.userName, COUNT(idThreadSubs) AS subs, COUNT(idThreadReplies) as replies
FROM threadpost
JOIN threadcategories
ON idthreadcategories = threadpost.category
JOIN users
ON idUsers = UserId
LEFT JOIN threadsubs
ON threadpost.idThreadPost = threadsubs.ThreadId
LEFT JOIN threadreplies
ON threadpost.idThreadPost = threadreplies.ThreadId
WHERE idthreadcategories LIKE ?
GROUP BY idThreadPost
ORDER BY date desc
LIMIT 20;`
the problem comes with adding COUNT(idThreadReplies). As you can see, I'm grouping by idThreadPost. This is because I want to retrieve both the count of subscriptions to the thread, and the count of replies.
However, the result gives me the incorrect number of replies (the same number as subscriptions).
How would I formulate this query correctly?
figured it out. solution is to use subqueries in the joins that require group by:
`SELECT subject, threadpost.date, threadpost.idThreadPost as id,
threadcategories.category, users.userName, tsubs.subs AS subs, trep.replies as replies
FROM threadpost
JOIN threadcategories
ON idthreadcategories = threadpost.category
JOIN users
ON idUsers = UserId
LEFT JOIN (
SELECT threadsubs.ThreadId as tsubId, COUNT(idThreadSubs) as subs
FROM threadsubs
GROUP BY idThreadSubs
) as tsubs
ON tsubs.tsubId = threadpost.idThreadPost
LEFT JOIN (
SELECT threadreplies.ThreadId as tId, COUNT(threadreplies.idThreadReplies) as replies
FROM threadreplies
GROUP BY threadreplies.ThreadId
) AS trep
ON trep.tId = threadpost.idThreadPost
WHERE idthreadcategories LIKE ?
ORDER BY date desc
LIMIT 20;`

SQL query that limits the results to one when using count inside count

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!

Generating user post count and thread count from different tables

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

How to make sql query?

Here is the structure of table Likes:
id(int 11)
idn(varchar 10)
type (enum ('up','down'))
Here is the structure of table News:
id(int 11)
idn(varchar 10)
header (varchar 100)
date(datetime)
To show news, I use sql query:
SELECT header FROM News n left join Likes l on l.idn=n.idn ORDER by n.date
But now I want to select headers news From table News with order by desc count rows in table Likes with type = up.
How can I do this?
Here is a query to get top liked headers for each day. If you need TOP for all dates remove n.date from the ORDER BY:
SELECT header FROM News as n
LEFT JOIN
(
Select idn,Count(*) as UpLikes FROM Likes WHERE type='up' GROUP BY idn
) as l
ON l.idn=n.idn
ORDER BY n.date,l.UpLikes DESC
Why don't you use this:
SELECT id, idn, header, LikeCount
FROM
(
SELECT id, idn, header, (SELECT COUNT(1) FROM [Likes]) AS [LikeCount]
FROM [News]
)
ORDER By [LikeCount]
SELECT n.header, COUNT(i.`id`) as like_count
FROM Likes l LEFT JOIN News n ON n.idn = l.idn
WHERE l.`type` = 'up'
ORDER BY like_count DESC

Counting rows from a subquery

How could I count rows from a SELECT query as a value?
Such as
SELECT FUCNTIONIMLOOKINGFOR(SELECT * FROM anothertable) AS count FROM table;
So that count is an integer of how many rows the subquery SELECT * FROM anothertable returns.
EDIT
SELECT p.PostPID, p.PostUID, p.PostText, p.PostTime, u.UserUID, u.UserName, u.UserImage, u.UserRep,
(
SELECT COUNT(f.FlagTime)
FROM Flags as f
JOIN Posts as p
ON p.PostPID = f.FlagPID
) as PostFlags
FROM Posts AS p
JOIN Users AS u
ON p.PostUID = u.UserUID
ORDER BY PostTime DESC
LIMIT 0, 30
SELECT ( SELECT COUNT(id) FROM aTable ) as count FROM table
I assume your example is a truncated version of your actual query, so perhaps you should post what you are after to get a, possibly, more optimal query.
EDIT
Working directly from my brain, something like this should be more optimal.
SELECT p.PostPID, p.PostUID, p.PostText, p.PostTime, u.UserUID, u.UserName, u.UserImage, u.UserRep, COUNT(v.FlagTime) as postFlags
FROM Flags as f
JOIN Posts as p ON p.PostPID = f.FlagPID
JOIN Users AS u ON p.PostUID = u.UserUID
LIMIT 0, 30
GROUP BY p.PostPID
ORDER BY PostTime DESC
You can say
SELECT COUNT(*) FROM anothertable
which will return a numeric value, which you can use in another query, such as in the select list of another query, or as a condition in another query.
SELECT someVariable FROM table
WHERE (SELECT COUNT(*) FROM anotherTable) > 5
OR
SELECT someVariable, (SELECT COUNT(*) FROM anotherTable) as count FROM table