Postgres - Left join using a where clause + distinct - sql

I want to join two tables using a join
SELECT * FROM posts
LEFT JOIN voted ON posts.post_id = voted.id
Which produces this:
How would I create query using:
ORDER BY date_posted DESC FETCH FIRST 5 ROW ONLY
on the Posts Table to return this result
Edit 1: duplicate post_id
How would I make it so that the uuid on the user_id column is only 82411850-
Edit 2: Final query thanks to Mr.Linoff
SELECT p.post_id, p.date_posted, p.posted_by,
v.user_id, v.votes
FROM posts p LEFT JOIN
voted v
ON p.post_id = v.id
AND v.user_id = '82411580...'
ORDER BY p.date_posted DESC
FETCH FIRST 5 ROW ONLY ;

You have a collision of ids. Be explicit about the columns you are selecting.
Then I think you have basically the right logic:
SELECT p.post_id, p.date_posted, p.posted_by,
v.user_id, v.votes
FROM posts p LEFT JOIN
voted v
ON p.post_id = v.id
ORDER BY p.date_posted DESC
FETCH FIRST 5 ROW ONLY ;

Related

Optimizing a nested SQL query through (preferably) joins

I am currently trying to fetch a list of Posts from a database, along with the likes, dislikes and checking whether the user has liked the post or not.
What I have tried:
Here's what the first version of the query looked like:
SELECT
announcements.*,
users.FIRSTNAME,
users.LASTNAME,
((SELECT COUNT(USER_ID) FROM likes_posts WHERE POST_ID = announcements.ID) - (SELECT COUNT(USER_ID) FROM dislikes_posts WHERE POST_ID = announcements.ID)) as TLIKES,
(SELECT COUNT(USER_ID) FROM likes_posts WHERE USER_ID = ? AND POST_ID = announcements.ID) AS USER_LIKED,
(SELECT COUNT(USER_ID) FROM dislikes_posts WHERE USER_ID = ? AND POST_ID = announcements.ID) AS USER_DISLIKED FROM announcements LEFT JOIN users ON announcements.OWNER_ID = users.ID
WHERE announcements.CHANNEL = ? AND announcements.ID < ? ORDER BY announcements.ID DESC
I have tried optimizing it through serval JOINS, but the results are quite messed up:
SELECT
announcements.*,
users.FIRSTNAME,
users.LASTNAME,
COUNT(likes_posts.USER_ID) AS TLikes,
COUNT(dislikes_posts.USER_ID) AS TDislikes,
UserLiked.ID AS userLiked,
UserDisliked.ID AS userDisliked
FROM announcements
LEFT JOIN likes_posts ON likes_posts.POST_ID = announcements.ID
LEFT JOIN dislikes_posts ON dislikes_posts.POST_ID = announcements.ID
LEFT JOIN likes_posts AS UserLiked ON UserLiked.USER_ID = ?
LEFT JOIN likes_posts AS UserDisliked ON UserDisliked.USER_ID = ?
LEFT JOIN users ON announcements.OWNER_ID = users.ID
WHERE announcements.CHANNEL = ? AND announcements.ID < ?
GROUP BY announcements.ID
ORDER BY announcements.ID DESC
Queries' results
The first query manages to constantly fetch the correct number of likes and dislikes (example: 5 and 3).
For the second one, however, it constantly fetches a number that is the double of the current likes or dislikes, whichever is bigger (eg. if there are 5 likes and 6 dislikes, the result would be 16 likes and 16 dislikes)
Problem
I'm guessing the second query is somehow fetching the likes_posts table 2 times, which causes the discrepancy between the likes and dislikes.
Here's one way you could do it, by aggregating the like and dislike counts first, then joining them to the base table. This way you're only doing the counts once each instead of twice
SELECT
a.*,
u.FIRSTNAME,
u.LASTNAME,
coalesce(likes.cnt, 0) - coalesce(dislikes.cnt, 0) as TLIKES,
coalesce(likes.cnt, 0) AS USER_LIKED,
coalesce(dislikes.cnt, 0) AS USER_DISLIKED
FROM
announcements a
LEFT JOIN
users u ON a.OWNER_ID = u.ID
left join
(
select post_id, count(user_id) cnt
from likes_posts
group by post_id
) likes on likes.post_id = a.id
left join
(
select post_id, count(user_id) cnt
from dislikes_posts
group by post_id
) dislikes on dislikes.post_id = a.id
WHERE
announcements.CHANNEL = ? AND announcements.ID < ?
ORDER BY
announcements.ID DESC

How to rewrite this without duplication, using Standard SQL BIG Query syntax?

Merging two tables the time entries table onto the user's table. Currently using Big query standard SQL. The column id is supposed to have 1 unique id for each entry but yet it pulls multiple of the same id # My Question how to rewrite this query without receiving duplications in the results? How to use LEFT Join with UNION ALL or UNION DISTINCT?
--*** Gives Duplications for some reason ***
SELECT outer_e.hours, outer_e.id, outer_e.updated_at, outer_e.spent_date, u.first_name, u.is_active, u.id AS user_id, u.weekly_capacity FROM
(SELECT e.id, MAX(e.updated_at) AS updated_at FROM `harvest-experiment.harvest.time_entries` AS e
GROUP BY e.id LIMIT 1000) AS inner_e
LEFT JOIN `harvest-experiment.harvest.time_entries` AS outer_e
ON inner_e.id = outer_e.id AND inner_e.updated_at = outer_e.updated_at
LEFT JOIN `harvest-experiment.harvest.users` AS u
ON outer_e.user_id = u.id
I was missing the DISTANT Keyword next my SELECT keyword, by doing so seems to fix the duplication problem in the views.
__***** Current Solution *****———
--*** Returns a Left Joined Table of `time entries` and `users` ***
SELECT DISTINCT outer_e.hours, outer_e.id, outer_e.updated_at, outer_e.spent_date, outer_e.created_at, outer_e.client_id, u.is_admin, u.first_name, u.is_active, u.id AS user_id, u.weekly_capacity, client.name FROM
(SELECT e.id, MAX(e.updated_at) AS updated_at FROM `harvest-experiment.harvest.time_entries` AS e
GROUP BY e.id LIMIT 1000) AS inner_e
LEFT JOIN `harvest-experiment.harvest.time_entries` AS outer_e
ON inner_e.id = outer_e.id AND inner_e.updated_at = outer_e.updated_at
LEFT JOIN `harvest-experiment.harvest.users` AS u
ON outer_e.user_id = u.id

i want to modify this SQL statement to return only distinct rows of a column

select
picks.`fbid`,
picks.`time`,
categories.`name` as cname,
options.`name` as oname,
users.`name`
from
picks
left join categories
on (categories.`id` = picks.`cid`)
left join options
on (options.`id` = picks.oid)
left join users
on (users.fbid = picks.`fbid`)
order by
time desc
that query returns a result that like:
my question is.... I would like to modify the query to select only DISTINCT fbid's. (perhaps the first row only sorted by time)
can someone help with this?
select
p2.fbid,
p2.time,
c.`name` as cname,
o.`name` as oname,
u.`name`
from
( select p1.fbid,
min( p1.time ) FirstTimePerID
from picks p1
group by p1.fbid ) as FirstPerID
JOIN Picks p2
on FirstPerID.fbid = p2.fbid
AND FirstPerID.FirstTimePerID = p2.time
LEFT JOIN Categories c
on p2.cid = c.id
LEFT JOIN Options o
on p2.oid = o.id
LEFT JOIN Users u
on p2.fbid = u.fbid
order by
time desc
I don't know why you originally had LEFT JOINs, as it appears that all picks must be associated with a valid category, option and user... I would then remove the left, and change them to INNER joins instead.
The first inner query grabs for each fbid, the FIRST entry time which will result in a single entity for the FBID. From that, it re-joins to the picks table for the same ID and timeslot... then continues for the rest of the category, options, users join criteria of that single entry.
2 options, you could write a group by clause.
Or you could write a nested query joined back to itself to get pertinent info.
Nested aliased table:
SELECT
n.fBids
FROM
MyTable t
INNER JOIN
(SELECT DISTINCT fBids
FROM MyTable) n
ON n.ID = t.ID
Or group by option
SELECT fBId from MyTable
GROUP BY fBID
select picks.`fbid`, picks.`time`, categories.`name` as cname,
options.`name` as oname, users.`name` from picks left join categories
on (categories.`id` = picks.`cid`) left join options on (options.`id` = picks.oid)
left join users on (users.fbid = picks.`fbid`)
order by time desc GROUP BY picks.`fbid`
select
picks.fbid,
MIN(picks.time) as first_time,
MAX(picks.time) as last_time
from
picks
group by
picks.fbid
order by
MIN(picks.time) desc
However, if you want only distinct fbid's you cannot display cname and other columns at the same time.

TSQL left join and only last row from right

I'm writing sql query to get post and only last comment of this post(if exists).
But I can't find a way to limit only 1 row for right column in left join.
Here is sample of this query.
SELECT post.id, post.title,comment.id,comment.message
from post
left outer join comment
on post.id=comment.post_id
If post has 3 comments I get 3 rows with this post, but I want only 1 row with last comment(ordered by date).
Can somebody help me with this query?
SELECT post.id, post.title, comment.id, comment.message
FROM post
OUTER APPLY
(
SELECT TOP 1 *
FROM comment с
WHERE c.post_id = post.id
ORDER BY
date DESC
) comment
or
SELECT *
FROM (
SELECT post.id, post.title, comment.id, comment.message,
ROW_NUMBER() OVER (PARTITION BY post.id ORDER BY comment.date DESC) AS rn
FROM post
LEFT JOIN
comment
ON comment.post_id = post.id
) q
WHERE rn = 1
The former is more efficient for few posts with many comments in each; the latter is more efficient for many posts with few comments in each.
Subquery:
SELECT p.id, p.title, c.id, c.message
FROM post p
LEFT join comment c
ON c.post_id = p.id AND c.id =
(SELECT MAX(c2.id) FROM comment c2 WHERE c2.post_id = p.id)
You'll want to join to a sub-query that returns the last comment for the post. For example:
select post.id, post.title. lastpostid, lastcommentmessage
from post
inner join
(
select post.id as lastpostid, max(comment.id) as lastcommentmessage
from post
inner join comment on commment.post_id = post.id
group by post.id
) lastcomment
on lastpostid = post.id
Couple of options....
One way is to do the JOIN on:
SELECT TOP 1 comment.message FROM comment ORDER BY comment.id DESC
(note I'm assuming that comment.id is an Identity field)
what version of SQL Server? If you have the Row_Number() function available you can sort your comments by whatever "first" means to you and then just add a "where RN=1" clause. Don't have a handy example or the right syntax off the top of my head but do have tons of queries that do exactly this. Other posts are all in the 1,000's of ways you could do this.
I'd say profile it and see which one performs best for you.
You didn't say the specific name of your date field, so I filled in with [DateCreated]. This is essentially the same as AGoodDisplayName's post above, but using the date field instead of relying on the ID column ordering.
SELECT post.id, post.title, comment.id, comment.message
FROM post p
LEFT OUTER JOIN comment
ON comment.id = (
SELECT TOP 1 id
FROM comment
WHERE p.id = post_id
ORDER BY [DateCreated] ASC
)

Help with SQL Join on two tables

I have two tables, one is a table of forum threads. It has a last post date column.
Another table has PostID, UserId, and DateViewed.
I want to join these tables so I can compare DateViewed and LastPostDate for the current user. However, if they have never viewed the thread, there will not be a row in the 2nd table.
This seems easy but I cant wrap my head around it. Advice please.
Thanks in advance.
What is it that you're trying to do specifically - determine if there are unread posts?
You just need to use an outer join:
SELECT p.PostID, p.LastPostDate, ...,
CASE
WHEN v.DateViewed IS NULL OR v.DateViewed < p.LastPostDate THEN 1
ELSE 0
END AS Unread
FROM Posts p
LEFT JOIN PostViews v
ON v.PostID = p.PostID
AND v.UserID = #UserID
Note that I've placed the UserID test in the JOIN condition; if you put it in the WHERE predicate then you'll get no results because there will be no matching rows in the PostViews table.
So you're thinking something like:
SELECT t.UserID, t.PostID, t.LastPostDate, v.DateViewed
FROM dbo.Threads t
LEFT JOIN dbo.Views v ON v.PostID = t.PostID
AND v.UserID = t.UserID
WHERE t.UserID = #user;
v.DateViewed will be NULL if there's no corresponding row in Views.
If you have lots of rows in Views, you may prefer to do something like:
SELECT t.UserID, t.PostID, t.LastPostDate, v.DateViewed
FROM dbo.Threads t
CROSS APPLY (SELECT MAX(vw.DateViewed) as DateViewed
FROM dbo.Views vw
WHERE vw.PostID = t.PostID
AND vw.UserID = t.UserID
) v
WHERE t.UserID = #user;
The key is to use a LEFT JOIN, which will cause non-existent rows on the right side to come up as all NULL:
SELECT threads.lastpostdate, posts.dateviewed
FROM threads
LEFT JOIN posts
ON threads.id=posts.postid