I have two tables:
Users: ID, first_name, last_name
Networks: user_id, friend_id, status
I want to select all values from the users table but I want to display the status of specific user (say with id=2) while keeping the other ones as NULL. For instance:
If I have users:
? first_name last_name
------------------------
1 John Smith
2 Tom Summers
3 Amy Wilson
And in networks:
user_id friend_id status
------------------------------
2 1 friends
I want to do search for John Smith for all other users so I want to get:
id first_name last_name status
------------------------------------
2 Tom Summers friends
3 Amy Wilson NULL
I tried doing LEFT JOIN and then WHERE statement but it didn't work because it excluded the rows that have relations with other users but not this user.
I can do this using UNION statement but I was wondering if it's at all possible to do it without UNION.
You need to put your condition into the ON clause of the LEFT JOIN.
Select
u.first_name,
u.last_name,
n.status
From users u
Left Join networks n On ( ( n.user_id = 1 And n.friend_id = u.id )
Or ( n.friend_id = 1 And n.user_id = u.id )
Where u.id <> 1
This should return you all users (except for John Smith) and status friend if John Smith is either friend of this user, or this user is friend of John Smith.
You probably don't need a WHERE clause, and instead of that, put the condition into the "ON" clause that follows your "LEFT JOIN". That should fix your issues. Also, make sure that the main table is on the left side of the left join, otherwise, you should use a right join.
In addition to the (correct) replies above that such conditions should go in the ON clause, if you really want to put them in the WHERE clause for some reason, just add a condition that the value can be null.
WHERE (networks.friendid = 2 OR networks.friendid IS NULL)
From what you've described, it should be a case of joining a subset of networks to users.
select id, first_name, last_name, status
from users u
left join networks n on u.id = n.user_id
and n.friend_id = 1
where id <> 1;
The left join will keep rows from users that do not have a matching row in networks and adding the and n.friend_id = 1 limits when the 'friends' status is returned. Lastly, you may choose to exclude the row from users that you are running the query for.
Related
I have two tables, user:
id
full_name
1
Beatriz
2
Mauro
3
Jose
4
fran
approver :
id
subordinate_id
approver_id
1
1
2
2
3
4
I would like to bring up the names of people who are not registered in the subordinate_id column
I did the following query:
SELECT
U.full_name
FROM user AS U
INNER JOIN approver as A
ON U.id <> A.subordinate_id ;
enter image description here
and still users are coming in that are in the subordinate_id column of the approver table.
I would like to get the result only for user names that are not subordinate_id, can someone help with this?
I would like a result with only the users that are not subordinate_id
This is simple to accomplish an ansi-sql with a not exists semi join:
Select full_name
from user u
where not exists (
select * from approver a
where a.approver_id = u.id <-- or (subordinate_id, whichever it should be)
);
I have 2 tables
User:
ID
NAME
1
John
2
Jane
3
Jim
login:
id
date
1
2021-01-29
3
2021-02-27
1
2021-03-11
3
2021-04-18
I want to get the result like:
name
date
John
2021-03-11
Jane
null
Jim
2021-04-18
How shall I write the SQL query?
I tried quite a few join but never got the 2nd record (Jane/Null) out from the query. Need some help here, thanks a ton in advance!
You can use left join and group by:
select u.id, u.name, max(l.date)
from user u left join
login l
on l.id = u.id
group by u.id, u.name;
Note: This includes the id as well. If name is known to be unique that is not necessary.
complimenting #gordons answer another option is to use a subquery like follows
SELECT user.name, login.last_login
FROM user
LEFT JOIN (SELECT id, max(date) AS last_login FROM logins group by id) AS login
ON login.id = user.id
Tables
User
id
name
email
is_active
1
john
john#albert.com
FALSE
2
mike
mike#ss.com
TRUE
3
monica
monica#dunno.com
TRUE
4
joey
joey#as.com
FALSE
5
ross
ross#boss.com
FALSE
Subscriptions
id
house_id
plan name
status
1
1
A banana a month
inactive
2
2
An apple a month
active
3
3
A pear a month
active
House
id
name
1
John's House
2
Mike's House
3
Monica's House
4
Joey's House
5
Ross's House
House_Contact (legacy table)
id
house_id
is_primary
1
1
TRUE
2
2
FALSE
2
3
TRUE
House_User (new table)
id
house_id
is_owner
user_id
1
2
FALSE
2
2
4
FALSE
4
3
5
FALSE
5
Expected Results
The resulting table should include the following:
Does the user have a subscription regardless of status? If so, include, if not, disregard.
Get email & is_active from User table (if they have subscription)
Get is_primary OR is_owner (if they have a subscription)
Results should be distinct (no duplicate users)
house_id
email
is_owner
is_active
1
john#albert.com
TRUE
FALSE
2
mike#ss.com
FALSE
TRUE
3
monica#dunno.com
TRUE
TRUE
What I tried
SELECT
u.email AS "email",
u.is_active AS "is_active",
h.id AS "house_id",
is_owner
FROM
house c
INNER JOIN (
SELECT
house_id,
user_id
FROM
house_user) hu ON h.id = hu.house_id
INNER JOIN (
SELECT
id,
email,
is_active
FROM
USER) u ON hu.user_id = u.id
INNER JOIN (
SELECT
id,
email,
is_primary
FROM
house_contact) hc ON u.email = ch.email
INNER JOIN (
SELECT
house_id,
is_primary is_owner
FROM
house_contact
UNION
SELECT
house_id,
is_owner is_owner
FROM
house_user) t ON u.id = t.house_id)
ORDER BY
u.email
Results are half than if I remove the INNER JOIN with UNION statement. No idea how to proceed.
I'm particularly confused with unifying the column and the possible duplication.
My educated guess:
SELECT DISTINCT ON (u.id)
u.id, u.email, u.is_active, h.house_id, h.is_primary
FROM "user" u
LEFT JOIN (
SELECT hu.user_id, hu.house_id
, GREATEST(hc.is_primary, hu.is_owner) AS is_primary
FROM house_user hu
LEFT JOIN house_contact hc USING (house_id)
WHERE EXISTS (SELECT FROM subscription WHERE house_id = hu.house_id)
) h ON h.user_id = u.id
ORDER BY u.id, h.is_primary DESC NULLS LAST, h.house_id;
We don't need table house in the query at all.
I see three possible sources of conflict:
house_contact.is_primary vs. house_user.is_owner. Both seem to mean the same. The DB design is broken in this respect. Taking GREATEST() of both, which means true if either is true.
We don't care about subscription.status, so just make sure the house has at least one subscription of any kind with EXISTS, thereby avoiding possible duplicates a priori.
A user can live in multiple houses. We want only one row per user. So show the first house with is_primary (the one with the smallest house_id) if any. If there is no house, there is also no subscription. But the outer LEFT JOIN keeps the user in the result. Change to JOIN to skip users without subscription.
About DISTINCT ON:
Select first row in each GROUP BY group?
About sorting boolean values:
Sorting null values after all others, except special
Sort NULL values to the end of a table
You can use the joins as follows:
Select distinct hu.house_id, u.email, hu.is_owner, hc.is_primary
From user u join house_user hu on u.id = hu.user_id
Join subscriptions s on s.house_id = hu.house_id
Join house_contract hc on hc.house_id = s.house_id;
I have used distinct to remove duplicates if you have multiple data in the table for matching condition. You can remove it if not required in case it is not required.
From what I can tell, you want to start with a query like this:
select s.house_id, u.email, hu.is_owner, u.is_active
from subscriptions s left join
house_user hu
on s.house_id = hu.house_id left join
users u
on hu.user_id = u.id;
This does not return what you want, but it is rather unclear how your results are derived.
I have two tables (Users and Pairs). The Pairs table contains 3 columns, an ID and then a user1ID and user2ID.
Users
ID firstName surname
------------------------------
1043 john doe
2056 jane doe
Pairs
ID user1ID user2ID
------------------------------
1 1043 2056
I'm then looking at using a select statement to get the user details base on the ID of the Pairs table:
SELECT users1.*, users2.*
FROM Pairs
JOIN Users users1 ON Pairs.user1ID = users1.IDNumber
JOIN Users users2 ON Pairs.user2ID = users2.IDNumber
WHERE Pairs.ID = 1
Which returns the right details for the two users, however they're all on one row, how can I get it to return each user on a separate row as they are in the Users table?
SELECT users1.*, users2.*
FROM Pairs
JOIN Users
ON Pairs.user1ID = users.IDNumber
OR Pairs.user2ID = users.IDNumber
WHERE Pairs.ID = 1
Just use an OR statement in your ON condition instead of 2 joins.
IN will work also.
SELECT *
FROM Pairs p
JOIN Users u ON u.ID IN (p.user1ID, p.User2ID)
WHERE p.ID = 1
I have tables users and topics. Every user can have from 0 to several topics (one-to-many relationship).
How I can get only those users which have at least one topic?
I need all columns from users (without columns from topics) and without duplicates in table users. In last column I need number of topics.
UPDATED:
Should be like this:
SELECT user.*, count(topic.id)
FROM ad
LEFT JOIN topic ON user.id = topic.ad
GROUP BY user.id
HAVING count(topic.id) > 0;
but it takes 0 result. But it should not be 0.
Firstly you need to have your two tables, because you have left limited information about your table structure I will use an example to explain how this works, you should then be able to easily apply this to your own tables.
Firstly you need to have two tables (which you do)
Table "user"
id | name
1 | Joe Bloggs
2 | Eddy Ready
Table "topic"
topicid | userid | topic
1 | 1 | Breakfast
2 | 1 | Lunch
3 | 1 | Dinner
Now asking for a count against each user is done using the follwing;
SELECT user.name, count(topic.topicid)
FROM user
INNER JOIN topic ON user.id = topic.userid
GROUP BY user.name
If you use a left join, this will include records from the "user" table which does not have any rows in the "topic" table, however if you use an INNER JOIN this will ONLY include users who have a matching value in both tables.
I.e. because the user id "2" (which we use to join) is not listed in the topic table you will not get any results for this user.
Hope that helps!
use inner join and distinct
select distinct user_table.id
from user_table
inner join topics_table on topic_table.user_id = user_table.id
select u.id
, u.name
, count(b.topicName)
from user u
left join topic t on t.userid = u.id
group by u.id, u.name
You can select topic number per user and then join it with user data. Something like this:
with t as
(
select userid, count(*) as n
from topic
group by userid
)
SELECT user.*, t.n
FROM user
JOIN t ON user.id = t.userid