SQL query to find the friends in a table - sql

I have two tables, Users and Friends. The tables look like:
USERS
| ID | Name |
| 1 | Sam |
| 2 | Harry |
| 3 | Vivi |
| 4 | sabrina |
FRIENDS
| UId | FriendID|
| 1 | 2 |
| 2 | 3 |
| 4 | 1 |
| 5 | 4 |
| 1 | 3 |
I need to find the names of all the friends for Sam. I tried doing the same using a Union in an SQL query, but I couldn't get the desired output. Can I possibly get the required output doing the same?

declare
#answer nvarchar(max)='{'
select #answer=#answer+u1.Name+',' from USERS u
inner join FRIENDS f on f.UId=u.ID
inner join USERS u1 on u1.ID=f.FriendID
where u.ID=<what ever you want> //1 or 2 or 3 or 4
set #answer=SUBSTRING(#answer,0,len(#answer)-1)+'}'
select #answer

select u.name from users
join friends f on users.id=f.uid
join users u on u.id=f.friendid
where users.name='Sam';

Related

Postgres tags list and tag filtering

I have 3 tables
users table:
+---------+---------+
| id | name |
+---------+---------+
| 1 | James |
| 2 | Carl |
| 3 | Bob |
| 4 | Steve |
| 5 | Evan |
+---------+---------+
tags table:
+---------+---------+
| id | name |
+---------+---------+
| 1 | Travel |
| 2 | Food |
| 3 | Fitness |
+---------+---------+
usertag table:
+----------+----------+
| user_id | tag_id |
+----------+----------+
| 1 | 1 |
| 2 | 2 |
| 1 | 3 |
| 3 | 2 |
| 3 | 1 |
| 4 | 3 |
| 5 | 1 |
+----------+----------+
I made a query that fetches all the users and their tags in a comma-separated field
SELECT users.*, ARRAY_AGG(usertag.tag_id) as tags
FROM users
JOIN usertags on users.id = usertag.user_id
GROUP BY users.id
Which gives me the result:
+---------+---------+----------+
| id | name | tags |
+---------+---------+----------+
| 1 | James | [1,3] |
| 2 | Carl | [2] |
| 3 | Bob | [2,1] |
| 4 | Steve | [3] |
| 5 | Evan | [1] |
+---------+---------+----------+
What I need to do next is filter by tags. Very much an all-or-nothing type of filtering. So if I want a user with tags of 1 and 3, I should only get back James. Even though Steve, Bob, and Evan have tags 1 or 3, only James has the combination of 1 and 3.
I tried the following but it doesn't give me the results I expect
SELECT users.*, ARRAY_AGG(usertags.tag_id) as tags
FROM users
JOIN usertags on users.id = usertags.user_id
WHERE usertags.tag_id IN (1,3)
GROUP BY users.id
But I get this back
+---------+---------+----------+
| id | name | tags |
+---------+---------+----------+
| 1 | James | [3,1] |
| 3 | Bob | [1] |
| 4 | Steve | [3] |
| 5 | Evan | [1] |
+---------+---------+----------+
When I would really just like to get the users that have all those tags. In this case, it would be one user, James.
+---------+---------+----------+
| id | name | tags |
+---------+---------+----------+
| 1 | James | [3,1] |
+---------+---------+----------+
How can I change this query to make it an all-or-nothing type of filtering where I pass in any number of tag ids?
UPDATE:
I should have been a little more clear. Say I pass in tags [1], then I would like all users that have tag 1 even if they have something else. However, if I check for tags [1,3], then I want only users that have tags 1 and 3 present in their tags list. So if I check for [1,3], I don't want users back that have JUST 1 in their tags list. And say I pass in [1,2,3] as tags, then I don't want users that have just [1,3] in their tags list but all users that have [1,2,3] even if they have others that aren't asked for.
If you want the users you have at least those two tags, you can use the overlaps operator &&
SELECT u.*, ARRAY_AGG(ut.tag_id) AS tags
FROM users u
JOIN usertags ut ON u.id = ut.user_id
GROUP BY u.id
HAVING ARRAY_AGG(ut.tag_id) && array[1,3];
If you need those users that have exactly those two tag (not more), then you can either an equality condition:
HAVING ARRAY_AGG(ut.tag_id order by ut.tag_id) = array[1,3];
Note the order by to make the order in both arrays identical.
Alternatively you can combine the #> and <# operators
HAVING ARRAY_AGG(ut.tag_id) #> array[1,3]
AND ARRAY_AGG(ut.tag_id) <# array[1,3]
Assuming you want users having tags 1 and 3, and only those tags, you may use:
SELECT u.*, ARRAY_AGG(ut.tag_id) AS tags
FROM users u
INNER JOIN usertags ut ON u.id = ut.user_id
GROUP BY u.id
HAVING MIN(ut.tag_id) <> MAX(ut.tag_id) AND
COUNT(*) FILTER (WHERE ut.tag_id NOT IN (1, 3)) = 0;

How to get additional column with row count using two different tables?

In my database I have usergroup, usergroup_user tables. I want to make a SQL query which can result something like result(group_id, name, date, users_count).
usergroup table
------------------
| group_id| name |
------------------
| 10 |test1 |
| 11 |test2 |
| 12 |test3 |
------------------
usergroup_user table
---------------------
|group_id | user_id |
---------------------
| 10 | 100 |
| 10 | 200 |
| 10 | 250 |
| 11 | 250 |
| 11 | 700 |
---------------------
I want to get this kind of a reusult
------------------------------
|group_id | name |users_count|
------------------------------
| 10 |test1 | 3 |
| 11 |test2 | 2 |
| 12 |test3 | 0 |
------------------------------
You simply do this with the group by, as per the following bellow.
SELECT U.group_id ,U.RoleName,COUNT(R.Id)USERCOUNT
FROM usergroup U
LEFT OUTER JOIN usergroup_user R ON R.group_id =U.group_id
GROUP BY U.group_id ,U.RoleName
select
group_id,
name,
(select count(user_id) from [dbo].[usergroup_user] where usergroup_user.group_id=[usergroup].group_id ) AS users_count
from [dbo].[usergroup]
SELECT usergroup.group_id ,usergroup.name,COUNT(usergroup_user.id) as users_count
FROM usergroup
LEFT OUTER JOIN usergroup_user ON usergroup.group_id =usergroup_user.group_id
GROUP BY usergroup.group_id ,usergroup.name

SQL get all Data from one table connected with another

I got an little DB with two tables Weeks and Users.
Every User is able to have a Week, so let's say he can have a Week but must not.
Every Week has a WeekNr. I want to do a table like that for an specific WeekNr, where all users are shown even those without a Week with that WeekNr:
-------------------------------------------+
| Users | KM Driven | Goal | CarID |
+----------+------------+------------------+
| Driver1 | 555 | Spain | 1 |
+----------+------------+------------------+
| Driver2 | 0 | 0 | 0 |
+----------+------------+------------------+
| Driver3 | 777 | Germany | 9 |
+----------+------------+------------------+
| Driver4 | 888 | UK | 86 |
+----------+------------+------------------+
If there is a user which have no Week for a WeekNr and I want all columns expected his name filled with 0. See Driver2 in table above for an example.
My query looks actually like that:
SELECT *
FROM User
INNER JOIN Week ON Week.UserId = User.UserID
WHERE WeekNr = 22;
I totally understand why I'm only getting only the drivers with weeks for the specific WeekNr, but I have no clue how to solve the issue with filling the empty one with 0 and all of this in one query.
I hope my question got clear.
Thanks for your help in advance!
EDIT:
My table look like this
Users:
---------------------------------+
| User | PW | UserID |
+----------+------------+--------+
| Driver1 | *** | 1 |
| Driver2 | *** | 2 |
| Driver3 | *** | 3 |
| Driver4 | *** | 4 |
+----------+------------+--------+
Weeks:
------------------------------------------------------+
| WeekNr | KM Driven | Goal | WeekID | UserID |
+----------+------------+-----------------------------+
| 22 | 555 | Spain | 1 | 1 |
| 22 | 0 | USA | 2 | 3 |
| 22 | 777 | Germany | 3 | 4 |
| 23 | 888 | UK | 44 | 2 |
+----------+------------+-----------------------------+
I hope to have correctly understood your question: your problem is in the INNER JOIN, you get only rows present in both the tables. Try with LEFT OUTER:
EDIT:
That should do the trick, given tha additional info :)
;WITH Drivers AS (SELECT u.[User], w.*
FROM Users u LEFT OUTER JOIN Weeks w On w.UserId = u.UserID
WHERE WeekNr = 22)
SELECT u.[User], ISNULL(d.[KM Driven], 0) [KM Driven] , ISNULL(d.Goal, 0) Goal --, *
FROM Users u LEFT OUTER JOIN Drivers d ON u.UserID = d.UserID
Is there a typo in your question? I presume CarID is meant to be a column of Weeks. If so,
SELECT
u.[User]
, ISNULL(w.[KM Driven], 0) [KM Driven]
, ISNULL(w.[Goal], '0') [Goal]
, ISNULL(w.[CarID], 0) [CarID]
FROM dbo.[Users] u
LEFT JOIN Weeks w ON u.UserID = w.UserID AND WeekNr = 22

Oracle query using 3 tables, sql

I have a problem with a query for Oracle with this scenario:
Table People
ID | Name
1 | juan
2 | pedro
3 | luis
Table Properties
ID | nombre_inmueble | FK to Table People
1 | house | 1
2 | garden | 1
3 | terrace | 1
4 | moto | 2
5 | jet | 2
Table Accessories
ID | accessories | FK Table Properties
1 | windows | 1
2 | doors | 1
3 | scale | 2
4 | plants | 3
5 | motor | 4
What I want is only the people who have Properties and that have ALL Accessories, in this case the output would be
1 | juan
What would be the query?
Your query will look like this:
SELECT *
FROM People P
WHERE EXISTS(
SELECT 1
FROM Properties T
WHERE T.PEOPLE= P.ID
)
AND NOT EXISTS(
SELECT 1
FROM Properties T
WHERE T.PEOPLE= P.ID
AND NOT EXISTS(
SELECT 1
FROM Accessories A
WHERE A.Properties = T.ID
)
);

how to sql query 2 tables and group by?

i have 2 tables: activities and users.
users has columns: name, active
activities: name, type, time, user_id.
for example i have these tables:
users
-----
id | name | active
1 | marc | true
2 | john | true
3 | mary | true
4 | nico | true
activities
-----
id | name | type | time | user_id
1 | morn | walk | 90 | 2
2 | morn | walk | 22 | 2
3 | morn | run | 12 | 2
4 | sat | walk | 22 | 1
5 | morn | run | 13 | 1
6 | mond | walk | 22 | 3
7 | morn | walk | 22 | 2
8 | even | run | 42 | 1
9 | morn | walk | 22 | 3
10 | morn | walk | 62 | 1
11 | morn | run | 22 | 3
now i would like to get table that would sum time spent on each type of activity and would group it by user name. so:
result
------
user name | type | time
marc | walk | 84
marc | run | 55
john | walk | 134
john | run | 12
mary | walk | 44
mary | run | 2
nico | walk | 0
nico | run | 0
how should i write this query to get this result?
thanks in advance
gerard
you can use coalesce to get 0 for empty activities and distinct to get all type of possible activities
select
u.name, c.type,
coalesce(sum(a.time), 0) as time
from (select distinct type from activities) as c
cross join users as u
left outer join activities as a on a.user_id = u.id and a.type = c.type
group by u.name, c.type
order by u.name, c.type
sql fiddle demo
Select u.name, a.type, SUM(a.time) FROM
activities a
LEFT JOIN users u
ON a.user_id = u.id
GROUP BY u.name, a.type
FIDDLE
Use this to get zero count as well
SELECT c.name,c.type,aa.time FROM
(Select u.id,u.name, b.type FROM
users u
CROSS JOIN (SELECT DISTINCT type FROM activities) b) c
LEFT JOIN (SELECT a.user_id, a.type, SUM(a.time) as time FROM
activities a
GROUP BY a.user_id, a.type) aa ON
aa.user_id = c.id and c.type = aa.type
Fiddle2
this might work :
select users.name,activities.type,sum(activities.time)
from users left join activities on users.id = activities.user_id
where users.active group by users.name,activities.type