I am just learning sql and I am creating a social network as a learning experience . I have 2 tables called Streams and Votes, Streams pulls user created content and Votes stores the content that people have liked . What I am trying to figure out is how can I return the data from both tables to check if a user a liked a particular post being shown . For instance this is how both my tables look . If you see they both have a field in common stream_id and they both have number 278. How can I do an inner join that checks to see if there are any common stream_ID in both tables ? This is the sql code that I use that gets me the Stream data
Query 1
select post,profile_id,
votes,id as stream_id FROM streams WHERE latitudes>=28.1036 AND
28.9732>=latitudes AND longitudes>=-81.8696 AND -80.8798>=longitudes
order by id desc limit 10
The User ID is 11 and both Streams.profile_id and Votes.my_id are the same field . I have tried this SQL query but this only returns in total 1 result . Again I would like to return all results from the Streams table which I do in query 1 and also add another column to the results from the Votes table where Votes.stream_id=Streams.ID because it'll show that the particular user has liked that post. Any hemp would be great
Query 2
select s.post,s.profile_id,
s.votes,s.id as stream_id, v.my_id as ID FROM streams s inner join Votes v on (s.id = v.stream_id) WHERE latitudes>=28.1036 AND
28.9732>=latitudes AND longitudes>=-81.8696 AND -80.8798>=longitudes
order by id desc limit 10
Streams
Votes
You need to use a LEFT OUTER JOIN instead of an INNER JOIN.
SELECT s.post, s.profile_id, s.votes, s.id as stream_id, v.my_id as ID
FROM streams s
LEFT OUTER JOIN Votes v on (s.id = v.stream_id) WHERE latitudes>=28.1036 AND
28.9732>=latitudes AND longitudes>=-81.8696 AND -80.8798>=longitudes
order by id desc limit 10
For more information about joins, look here.
I think that you are looking for a LEFT JOIN
select s.post,s.profile_id,
s.votes,s.id as stream_id, v.my_id as ID FROM streams s LEFT JOIN Votes v on (s.id = v.stream_id) WHERE latitudes>=28.1036 AND
28.9732>=latitudes AND longitudes>=-81.8696 AND -80.8798>=longitudes
order by id desc limit 10
Related
I have a table with users and a table with their posts. I do not understand how to calculate the number of users, for example, whose number of posts is less than 10.
select Users.DisplayName, count(Users.id) as Questions from Users
LEFT JOIN
Posts on Users.id=Posts.OwnerUserId
GROUP BY Users.DisplayName
HAVING (count(Users.Id)<10) or (count(Users.Id)>10 and count(Users.Id)<20)
If you want the number of users that have less than 10 records, than you can simply use a COUNT:
SELECT
COUNT(a.DisplayName)
FROM
Users a
LEFT JOIN Posts b ON a.id = b.OwnerUserId
HAVING
COUNT(b.Id) < 10
You can see that here using the data explorer.
I am trying to select some data from different tables using join.
First, here is my SQL (MS) query:
SELECT Polls.pollID,
Members.membername,
Polls.polltitle, (SELECT COUNT(*) FROM PollChoices WHERE pollID=Polls.pollID) AS 'choices',
(SELECT COUNT(*) FROM PollVotes WHERE PollVotes.pollChoiceID = PollChoices.pollChoicesID) AS 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID;
And the tables involved in this query is here:
The query returns this result:
pollID | membername | polltitle | choices | votes
---------+------------+-----------+---------+-------
10000036 | TestName | Test Title| 2 | 0
10000036 | TestName | Test Title| 2 | 1
Any help will be greatly appreciated.
Your INNER JOIN with PollChoices is bringing in more than 1 row for a given poll as there are 2 choices for the poll 10000036 as indicated by choices column.
You can change the query to use GROUP BY and get the counts.
In case you don't have entries for each member in the PollVotes or Polls table, you need to use LEFT JOIN
SELECT Polls.pollID,
Members.membername,
Polls.polltitle,
COUNT(PollChoices.pollID) as 'choices',
COUNT(PollVotes.pollvoteId) as 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID
INNER JOIN PollVotes
ON PollVotes.pollChoiceID = PollChoices.pollChoicesID
AND PollVotes.memberID = Members.memberID
GROUP BY Polls.pollID,
Members.membername,
Polls.polltitle
You are getting 1 row for each PollChoices record since there are multiple choices per Polls INNER JOIN Members. You may be expecting the SELECT COUNT(*) sub-queries to act as a GROUP BY clause, but they don't.
If that doesn't make sense, add a bare minimum of sample data and the expected result and we can help more.
This query result is telling you the number of votes per choice in each poll.
In your example, this voter named TestName answered the poll (with ID 10000036) and gave one choice 1 vote, and the second choice 0 votes. This is why you are getting two rows in your result.
I'm not sure if you are expecting just one row because you didn't specify what data, exactly, you are trying to select. However if you are trying to see the number of votes that TestName has submitted, for each choice where the vote was greater than 1, then you will have to modify your query like this:
select * from
(SELECT Polls.pollID,
Members.membername,
Polls.polltitle, (SELECT COUNT(*) FROM PollChoices WHERE pollID=Polls.pollID) AS 'choices',
(SELECT COUNT(*) FROM PollVotes WHERE PollVotes.pollChoiceID = PollChoices.pollChoicesID) AS 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID) as mysubquery where votes <> 0;
I'm building a system in which there are the following tables:
Song
Broadcast
Station
Follow
User
A user follows stations, which have songs on them through broadcasts.
I'm building a "feed" of songs for a user based on the stations they follow.
Here's the query:
SELECT DISTINCT ON ("broadcasts"."created_at", "songs"."id") songs.*
FROM "songs"
INNER JOIN "broadcasts" ON "songs"."shared_id" = "broadcasts"."song_id"
INNER JOIN "stations" ON "broadcasts"."station_id" = "stations"."id"
INNER JOIN "follows" ON "stations"."id" = "follows"."station_id"
WHERE "follows"."user_id" = 2
ORDER BY broadcasts.created_at desc
LIMIT 18
Note: shared_id is the same as id.
As you can see I'm getting duplicate results, which I don't want. I found out from a previous question that this was due to selecting distinct on broadcasts.created_at.
My question is: How do I modify this query so it will return only unique songs based on their id but still order by broadcasts.created_at?
Try this solution:
SELECT a.maxcreated, b.*
FROM
(
SELECT bb.song_id, MAX(bb.created_at) AS maxcreated
FROM follows aa
INNER JOIN broadcasts bb ON aa.station_id = bb.station_id
WHERE aa.user_id = 2
GROUP BY bb.song_id
) a
INNER JOIN songs b ON a.song_id = b.id
ORDER BY a.maxcreated DESC
LIMIT 18
The FROM subselect retrieves distinct song_ids that are broadcasted by all stations the user follows; it also gets the latest broadcast date associated with each song. We have to encase this in a subquery because we have to GROUP BY on the columns we're selecting from, and we only want the unique song_id and the maxdate regardless of the station.
We then join that result in the outer query to the songs table to get the song information associated with each unique song_id
You can use Common Table Expressions (CTE) if you want a cleaner query (nested queries make things harder to read)
I would look like this:
WITH a as (
SELECT bb.song_id, MAX(bb.created_at) AS maxcreated
FROM follows aa
INNER JOIN broadcasts bb ON aa.station_id = bb.station_id
INNER JOIN songs cc ON bb.song_id = cc.shared_id
WHERE aa.user_id = 2
GROUP BY bb.song_id
)
SELECT
a.maxcreated,
b.*
FROM a INNER JOIN
songs b ON a.song_id = b.id
ORDER BY
a.maxcreated DESC
LIMIT 18
Using a CTE offers the advantages of improved readability and ease in maintenance of complex queries. The query can be divided into separate, simple, logical building blocks. These simple blocks can then be used to build more complex, interim CTEs until the final result set is generated.
Try by adding GROUP BY Songs.id
I had a very similar query I was doing between listens, tracks and albums and it took me a long while to figure it out (hours).
If you use a GROUP_BY songs.id, you can get it to work by ordering by MAX(broadcasts.created_at) DESC.
Here's what the full SQL looks like:
SELECT songs.* FROM "songs"
INNER JOIN "broadcasts" ON "songs"."shared_id" = "broadcasts"."song_id"
INNER JOIN "stations" ON "broadcasts"."station_id" = "stations"."id"
INNER JOIN "follows" ON "stations"."id" = "follows"."station_id"
WHERE "follows"."user_id" = 2
GROUP BY songs.id
ORDER BY MAX(broadcasts.created_at) desc
LIMIT 18;
Two questions.
One: How would I be able to join two tables together, which are not the original FROM 'x' table?
Example: Grabbing all the reports and that users level name.
Report
id | user_id
1 1
User
id | level_id
1 1
Level
id | level_name
1 Admin
SELECT report.id,
report.user_id,
level.name
FROM report
INNER JOIN user
ON report.user_id=user.id
INNER JOIN level
ON user.level_id=level.id
Doesn't seem to work.
Two: How to join tables not from a 1-1 but a 1-many?
Say I wanted to:
SELECT * FROM user JOIN report ON user.id=report.user_id WHERE user.id='4'
But only join with the most recent report ordered by date desc?
I know it seems like I am asking you to do my work for me, but I just need to know what to use to accomplish this, not necessarily you coding it. Thanks.
UPDATE:
First question: Fixed, thanks
Second question:
I'll show you my query to make better sense, I didn't explain it well. I meant a many-many relationship where I grab a list of users and their latest report!
SELECT
user.id
FROM
user
<< INNER JOINS ON OTHER TABLES >>
INNER JOIN
(
SELECT
report.id
FROM
report
WHERE user.id=report.user_id
ORDER BY
date
DESC
LIMIT 1
)
For your second question:
MySQL-style:
SELECT *
FROM user
INNER JOIN report re
ON re.id = (SELECT id
FROM report
WHERE user_id=user.id
ORDER BY report.date DESC
LIMIT 1)
WHERE user.id='4'
One: Looks good to me, but shouldn't that be level.level_name in the SELECT clause?
Two: If you just want the most recent report it would be something like...
SELECT TOP 1 * FROM user JOIN report ON user.id=report.user_id WHERE user.id='4' order by report.date desc
Basically, what I want to do is join 4 tables together and return 1 row for each boat.
Table Layouts
[Boats]
id, date, section, raft
[Photos]
id, boatid, pthurl, purl
[River_Company]
id, sort, company, company_short
[River_Section]
id, section
Its very simple as far as structure, however, I've having the time of my life trying to get it to return only 1 row. No boat will ever be on the same day, the only thing that's messing this up is the photo table.
If you know a better way for it to return the record table for all the boats boats and only 1 photo from the photo table, please, please post it!!
Desired Format
boats.id, boats.date, river_company.company, river_section.section, photos.purl, photos.pthurl
It's basically how joins work. Since boats and photos are in one-to-many relationships and you want one-to-one-like query, you need to explicitly express it with predicate. For example:
select b.*
from
boats b
inner join photos p
on b.id = p.boatid
where p.id = (select max(id) from photos where boatid = b.id)
Assuming your ID column is the relation that you have designed:
SELECT Boats.* FROM Boats
LEFT OUTER JOIN Photos on Photos.ID =
(
SELECT TOP 1 Photos.ID FROM Photos
INNER JOIN Boats ON Photos.BoatID = Boats.ID
)
INNER JOIN River_Company on River_Company.ID = Boats.ID
INNER JOIN River_Section on River_Section.ID = Boats.ID
So basically, this will:
Guarantee the maximum row count of 1. (It's a bit dirty, but fact is if you have more than one photo, more than one link will be returned otherwise)
If there are no photo's, the boat will still be returned