Two inner joins return empty result - sql

I have 3 very simple tables, users, projects and starred_projects.
starred_projects connects the other 2 other tables by having a user id and a project id.
CREATE TABLE projects
(
project_id integer,
name character varying(50) COLLATE pg_catalog."default",
creator_id integer,
)
CREATE TABLE users
(
user_id integer,
username character varying(64) COLLATE pg_catalog."default" NOT NULL,
)
CREATE TABLE starred_projects
(
user_id integer NOT NULL,
project_id integer NOT NULL
)
For a given user (e.g. "foo") I would like to display all projects he starred. Is an INNER JOIN the correct way to go here? When I do so, my result is always empty. As far as I remember from university, I would need two of them, as in:
SELECT
p.name, u.username // display the name of the project and the user who created it
FROM
projects p
INNER JOIN
starred s
ON
p.creator_id = s.user_id
INNER JOIN
users u
ON
u.user_id = p.creator_id
WHERE
u.username = 'foo'; // display all starred projects of user foo
Can anyone help me on this?

Users aren't necessarily creators.
A more correct way would be:
SELECT
p.name, u.username // display the name of the project and the user who created it
FROM
projects p
INNER JOIN
starred s
ON
p.project_id = s.project_id
INNER JOIN
users u
ON
u.user_id = s.user_id
WHERE
u.username = 'foo';
In other words, the creator of the project is not necessarily the only user. So you use starred to figure out which users are starred in what projects. So you match starred.user_id with user.user_id and you match starred.project_id with project.project_id.
Users with users, and projects with projects.

You have your table name as starred_projects yet in your INNER JOIN you quote it as just starred. Is that correct?
I can't see anything else wrong with your code, are you able to provide examples of the data in each table?

Related

User with multiple roles and multiple teams database design

I am unable to get my head around this
I want to allow users to have multiple roles and multiple teams and maybe in the future add something else.
What is the most optimal way to design this?
Users
----------
user_id
first name
last name
roles
--------
role_id
user_id (fk)
role_name
teams
-----
team_id
user_id (fk)
team name
something_else
------
selse_id
user_id (fk)
selse_name
and then group everything in one table like this?
user_profile
---------------------
user_id - username - role_name - team_name - selse_name
I don't know if this should be fine or not, because I am thinking it might get complicated when I pass the data to a form on the client side.
Your schema design is similar to how I would do this. Where your question falls apart is in the last bit about grouping everything together.
The schema should be:
create table users (
user_id serial primary key,
first_name text not null,
last_name text not null,
-- other columns here
);
create table roles (
role_id serial primary key,
role_name text not null unique,
-- other columns here
);
create table user_role (
id serial primary key,
user_id int not null references users(user_id),
role_id int not null references roles(role_id),
unique(user_id, role_id)
);
Make the lookup and join tables for teams and something_else similarly.
The tricky part is pulling this data for use in a UI. You will need to aggregate the properties or else you will have multiple rows for each user. For example, if a user has three roles, belongs to two teams, and has four something_else rows, then joining will net you twenty-four rows for that user.
Assuming your UI will be web or some modern toolkit, the best way to retrieve this data would be as json. PostgreSQL can build that json for you:
with aggs as (
select u.user_id, u.first_name, u.last_name,
jsonb_agg(DISTINCT to_jsonb(r)) as roles,
jsonb_agg(DISTINCT to_jsonb(t)) as teams,
jsonb_agg(DISTINCT to_jsonb(s)) as something_else
from users u
left join user_role ur on ur.user_id = u.user_id
left join roles r on r.role_id = ur.role_id
left join user_team ut on ut.user_id = u.user_id
left join teams t on t.team_id = ut.team_id
left join user_something_else us on us.user_id = u.user_id
left join something_else s on s.something_else_id = us.something_else_id
group by u.user_id, u.first_name, u.last_name
)
select to_jsonb(aggs)
from aggs
where user_id = ?
;

SQL Oracle - Multiple count queries on multiple tables

Maybe for some people it might look very simple, but I just cant get it.
My tables are:
CREATE TABLE USERS (user_ID number PRIMARY KEY, username varchar2(32), password varchar2(32));
CREATE TABLE VIDEOS (video_ID number PRIMARY KEY, title varchar(64), description varchar(128));
CREATE TABLE VIEWS (view_ID number PRIMARY KEY, user_ID number, video_ID number);
CREATE TABLE FAVORITES (fav_ID number PRIMARY KEY, user_ID number, video_ID number);
I ve created those separated queries:
SELECT u.username AS "Username", count(*) AS "Views"
FROM Views v, Videos vd, Users u
WHERE v.user_id = u.user_id
AND v.video_id = vd.video_id
GROUP BY u.username
SELECT u.username AS "Username", count(*) AS "Favorites"
FROM Favorites f, Videos vd, Users u
WHERE f.user_id = u.user_id
AND f.video_id = vd.video_id
GROUP BY u.username
And I want a query to show something like that in only one simple query:
Username Views Favorites
-------------------------------
Person1 12 1
Person2 234 21
...
I Googled bunch of similar questions but I couldnt make any of them to work.
So any help is greatly appreciated.
You are progressing on the right track. -> You got two queries and you wish to see them together. You could perform a full outer join to get your results you are looking for as below.
with fave
as (
SELECT u.username AS "Username"
, count(*) AS "Favorites"
FROM Favorites f
JOIN Videos vd
ON f.video_id = vd.video_id
JOIN Users u
ON f.user_id = u.user_id
GROUP BY u.username
)
,views
as (SELECT u.username AS "Username"
, count(*) AS "Views"
FROM Views v
JOIN Videos vd
ON v.video_id = vd.video_id
JOIN Users u
ON v.user_id = u.user_id
GROUP BY u.username
)
select isnull(f.username,v.username) as username
,f.favourites
,v.views
from fave f
full outer join views v
on f.username=v.username
Since you know your data better, you could optimize the query further. Eg: it could be a rule that user who has set a favourite would also have viewed the video. If this is true then you can write a better query to optimize the dataset in a single block, instead of two blocks using full outer join
Aggregate separately in Views:
select user_id, count(*) counter
from Views
group by user_id
and Favorites
select user_id, count(*) counter
from Favorites
group by user_id
and finally LEFT join Users to the above queries:
select u.username,
coalesce(v.counter, 0) Views,
coalesce(f.counter, 0) Favorites
from users u
left join (
select user_id, count(*) counter
from Views
group by user_id
) v on v.user_id = u.user_id
left join (
select user_id, count(*) counter
from Favorites
group by user_id
) f on f.user_id = u.user_id
I used LEFT joins because there may exist users that did not see any video or do not have any favorites. In any of these cases COALESCE() will return 0 instead of null.
The table Videos is not needed.

SQL Query With Join for 2 Tables

I'm unable to form a query with the following tables, which will find out all the Notes from Note table, which is created by any user, who belongs to the logged in user's same company.
Note:
note_id (int),
note_text (varchar),
created_by (int)
User:
user_id (int),
company_id (int)
Logged in user's user id is passed as parameter to the query.
I want to pick up notes from the table Notes where created_by in (user_id of all users whose company_id = company_id of LOGGED_IN_USER)
Please help me to formulate out the query. Looks pretty straight forward, but just can't reach to it's end.
I'm not sure if LOGGED_IN_USER is a table or another object, but if it is a table with the columns you referenced, a join like this would work.
select note_text
from Note n
JOIN User u ON u.user_id = n.created_by
JOIN LOGGED_IN_USER lin ON lin.user_id = u.user_id
and lin.company_id = u.company_id
u might need a Foreign Key for user_id to Notes. and use INNER JOIN
Thanks Vinnie and all for your responses. I finally succeeded to figure out the query. LOGGED_IN_USER_ID is just a numeric parameter which should be passed to the query.
select n.* from Note n where
n.created_by in (
select u1.user_id from User u1 inner join User u2
on u1.company_id=u2.company_id and u2.user_id = :LOGGED_IN_USER_ID*
)
Thanks again.
If i am not misunderstood your question,Try this way
SELECT note_text
FROM Note n
INNER JOIN User u ON u.user_id = n.created_by
WHERE n.created_by= (select u.user_id from User where company_id=LOGGED_IN_USER LIMIT 1 )

Select user details from a self relationship table

I have 3 tables (user, relationship and user_type) with these data (the relevant ones):
USER TABLE:
id, username, avatar, user_type_id
RELATIONSHIP TABLE:
id, user_id1, user_id2, relationship_points
USER_TYPE
id
I'm trying to create a single entry per relationship so, user with id "1" could be in user_id1 OR user_id2 in a relationship, so, I don't have to duplicate unnecessary data.
I already created (thanks to another StackOverflow answer) a query to select all relationship details from an user, but only if it's id is in "user_id1".
SELECT
r.id AS relationship_id,
r.relationship_points AS points,
u.username AS username,
u.avatar_url AS avatar
FROM
relationship AS r
INNER JOIN
user AS u
ON
r.user_id2 = u.id
INNER JOIN
user_type AS t
ON
u.user_type_id = t.id
WHERE
r.user_id1 = ?
But, as you can see, if the user is in "user_id2", it doesn't work.
I know I could make another query, but I think it's the "easy, lazy" way, and I would love to learn how to do this in a single query.
Let me know if this makes the trick:
SELECT
r.id AS relationship_id,
r.relationship_points AS points,
u.username AS username,
u.avatar_url AS avatar
FROM relationship AS r
INNER JOIN user u
ON r.user_id2 = u.id OR r.user_id1 = u.id
INNER JOIN user_type AS t
ON u.user_type_id = t.id
WHERE u.id = ?

Select statement using primary key with no primary key

Ok so I have a table for notes in my database and a table for users in my database. Lets say notes table looks like this:
NoteId NoteTitle NoteAuth
--------------------------------
1 TempTitle 254
2 TempTitle2 871
and Users table looks like:
UserId UserName
-------------------
254 Bob
871 Jim
How can I select from the Note table but select the NoteAuth as the UserName from the Users table. Basically how can I use a primary key without a primary key being set up on the table.
SELECT NoteID, NoteTitle, UserName
FROM Notes INNER JOIN Users ON Notes.NoteAuth = Users.UserID
There's no requirement for any keys to exist to use a column in a JOIN.
You can try something like this
SELECT Notes.NoteId, Notes.NoteTitle, Notes.NoteAuth
FROM Notes
INNER JOIN Users ON Notes.NoteAuth = Users.UserId
WHERE Users.UserName = 'Bob'
Edit
Here is a version that returns the user name for a specified note author.
SELECT Users.UserName, Notes.NoteId, Notes.NoteTitle, Notes.NoteAuth
FROM Notes
INNER JOIN Users ON Notes.NoteAuth = Users.UserId
WHERE Notes.NoteAuth = 254
select n.NoteId, n.NoteTitle, n.NoteAuth, u.UserName
from Notes n
inner join User u on n.NoteAuth = u.UserID
The lack of a primary key will cause lack of table integrity. Now you won't be able to create a foreign key between the tables. This means that going forward you will be allowed to create a note with a user that doesn't exist in the Users table.
This won't stop you from
SELECT n.NoteTitle, u.UserName
FROM NoteTable n LEFT JOIN Users u ON n.NoteAuth = u.UserId
That will work in SqlServer.
The problem is is if you have a note that has a NoteAuth that doesn't exist in the users table, you'll end up with a NULL username. Use an ISNULL to replace NULL with something like 'No Username'.
But like I said, there won't be any integrity between the tables.
SELECT U.Username, N.NoteTitle from Notes N, Users U WHERE N.NoteAuth = U.UserId