I've a user table in my application.
Id Name ParentId
1 User1 0
2 User2 1
3 User3 1
4 User4 2
5 User5 2
The users have a relationship like grandparent->parent->child. to fetch the users related to a grand parent I'm using the following query
select * from user where ParentId=1 or ParentId in(select id from user where ParentId=1)
also these user have multiple roles in my role table and for fetching the count of rules a user having, I'm using select count(*) from group where userId=1.
Id Role UserId
1 Role 1 1
2 Role 2 1
3 Role 3 2
4 Role 4 2
5 Role 5 3
I need to fetch these both data in a single query .I'm not good with SQL and I know my first query is not perfect . How can I achieve this.
Your query is just need to be joined with your roles table.
SELECT u.id, count(r.id) no_of_roles
FROM user u left join roles r
on u.id = r.userid
WHERE u.ParentId=1
OR u.ParentId IN
(SELECT id FROM t WHERE ParentId=1
)
group by u.id;
Related
I have two tables:
user
id
full_name
is_admin
is_active
1
Alan
0
1
2
Carl
0
1
3
Any
0
1
4
Jane
0
1
5
Marry
0
1
6
Pedri
0
1
7
admin
1
1
8
Mota
0
0
approver
id
subordinate_id
leader_id
main_leader_id
is_active
1
1
2
3
0
2
4
5
6
1
3
1
2
4
0
(subordinate_id, leader_id and main_leader_id are foreign keys that correspond to the id column of the user table)
I would like to perform a query that brings all user names that are not admin (user table is_admin=0) and that are active (user table is_active=1), and that if they have the id in the subordinate_id column in the approver table that only brings the name of that user that has the is_active of the approver table = 0.
That is, I would like to bring users that if they have any record as subordinate_id that only bring me those that are not active in the approver table.
I tried to get the data in the following way:
SELECT
full_name
FROM user AS U
LEFT JOIN approver AS A
ON U.id = A.subordinate_id
WHERE
A.id is null
AND
U.is_admin = 0
AND
U.is_active = 1
But with this query i only get the user name that not has a register in the approver table,
and in my case i want to get the user that have a register in the approver table as subordinate_id, but not if the register have the column 'is_active' equal to 1.
In my final result I could get something like this:
Alan
carl
any
marry
Pedri
In order to make this working, you should split the conditions in the WHERE clause into:
"user" conditions: is_admin = 0 AND is_active = 1
"approver" conditions: is not a subordinate OR is_active = 0
These two groups of conditions have to be set in AND.
SELECT DISTINCT user_.id, user_.full_name
FROM user_
LEFT JOIN approver
ON user_.id = approver.subordinate_id
WHERE (user_.is_admin = 0 AND user_.is_active = 1)
AND (approver.id IS NULL OR approver.is_active = 0)
Check the demo here.
Note: the DISTINCT keyword is necessary because the JOIN operation is made between two tables having cardinality 1:n.
I have 2 resources: users and privileges - and there is also some connecting table like user_privilege
So my structure looks like this:
user
user_id, name
1 John
2 Daniel
3 Paul
privilege
privilege_id, name
1 Perm 1
2 Perm 2
3 Perm 3
user_privilege
user_id, privilege_id
1 1
1 2
2 1
And if there is record in user_privilege that means user has this privilege.
But I also want to select privileges which user doesn't have.
It's easy to select all given privileges
But I want output like:
privilege_id, user_id, has
1 1 true
2 1 true
3 1 false
1 2 true
2 2 false
3 2 false
1 3 false
2 3 false
3 3 false
So actually it's something like each privilege for each user - but with information if user has this privilege or not.
I want to to this in Posgres db.
You want a cross join between user and privilege and then do an outer join to user_privilege to test if that user has the privilege
select au.user_id, au.privilege_id,
up.privilege_id is not null as has_priv
from (
select u.user_id, p.privilege_id
from "user" u
cross join privilege p
) au
left join user_privilege up on (au.user_id, au.privilege_id) = (up.user_id, up.privilege_id)
order by user_id, privilege_id
Online example
I have a dataset of users who have liked each other. The data looks like
User HasLiked
User 1 User 3
User 1 User 4
User 1 User 5
User 2 User 3
User 3 User 1
User 4 User 1
User 4 User 5
User 5 User 3
User 5 User 4
I can get count of liked users for any given user using
select user, count(*) as liked_count
from users
group by user;
which gives me
User liked_count
User 1 3
User 2 1
User 3 1
User 4 2
User 5 2
However I can't figure out how to count the number of times a user is liked back by someone they have liked. e.g what I want is:
User liked_count liked_back_count
User 1 2 2
User 2 1 0
User 3 1 1
User 4 2 2
User 5 2 1
Any ideas how to do this using sql? FYI, The number of distinct users is around 100K.
You can use a left join to determine if the "inverse" record appears. And then aggregate:
select u.user, count(*) as liked_count,
count(*) filter (where u2.user is not null) as liked_back_count
from users u left join
users u2
on u2.user = u.hasliked and
u2.hasliked = u.user
group by u.user;
My "users" table is like this
id name
1 UserA
2 UserB
3 UserC
4 UserD
And my "chats" table is like this
id text sentBy sentTo created
1 Hi UserB 1 2 2019-01-11
2 Hi 2 1 2019-01-12
3 Hi UserB 3 2 2019-01-13
4 Hello UserC 2 3 2019-01-14
5 Hello 3 2 2019-01-15
6 What u do 2 1 2019-01-16
7 Nothing 1 2 2019-01-17
8 Okay 2 1 2019-01-18
8 Hi UserA 3 1 2019-01-19
I want to show user list who is involved in a conversation with logged in user based on the last msg.
Like if UserA logged in the system then the list should be like
userId userName text created
3 UserC Hi UserA 2019-01-19
2 UserB Okay 2019-01-19
I tried by using a join query and group by but not succeed.
I am using PostgreSQL with koa js.
You may use DISTINCT ON to filter out all apart from the most recent record for each involved in the conversation.
SELECT DISTINCT ON( involved) involved AS userid,
u.NAME AS username,
text,
created
FROM (SELECT c.*,
CASE sentby
WHEN 1 THEN sentto
ELSE sentby
END AS involved
FROM chats c
WHERE c.sentby = 1
OR c.sentto = 1) s
JOIN users u
ON s.involved = u.id
ORDER BY involved,
created DESC
Demo
If you want a generic case, you may convert this to a SQL type Postgres function and pass userid as an argument and use it in place of 1.
To get a complete list of users involved in the last conversation of the $current_user, with the last message (sent or received) and its date (created):
WITH u1 AS (SELECT id FROM users WHERE name = $current_user)
SELECT DISTINCT ON (userId) *
FROM (
SELECT u.id AS userId, u.name AS userName, c.text, c.created -- received
FROM u1
JOIN chats c ON c.sentBy = u1.id
JOIN users u ON u.id = c.sentTo
UNION ALL
SELECT u.id, u.name, c.text, c.created -- sent
FROM u1
JOIN chats c ON c.sentTo = u1.id
JOIN users u ON u.id = c.sentBy
) sub
ORDER BY userId, created DESC;
I separated into two UNIONed SELECTs, to make the most of two separate indexes on chats - one with leading sentTo, one with leading sentBy. Just updated a closely related answer yesterday:
Finding all rows with unique combination of two columns
About DISTINCT ON:
Select first row in each GROUP BY group?
Might be optimized in various ways, depending on undisclosed information.
I want to display all available users (user type: employee) on a given schedule date. They are not available if they are scheduled both day (PM/AM)
Here are my following tables:
User Types
TypeID TypeName
1 Admin
2 Employee
Users
UserID TypeID Name
1 1 Admin 1
2 2 Employee 1
3 2 Employee 2
4 1 Admin 2
5 2 Employee 3
6 2 Employee 4
7 2 Employee 5
Schedule
SchedID UserID SchedDate Day (PM/AM)
1 2 8/27/2013 PM
2 2 8/27/2013 AM
3 3 8/27/2013 AM
4 5 8/27/2013 PM
5 6 8/27/2013 AM
Expected Result (WHERE SchedDate='8/27/2013')
UserID Name
3 Employee 2
5 Employee 3
6 Employee 4
7 Employee 5
This is my current SQL statement:
SELECT Users.UserID, Users.Name FROM Users LEFT OUTER JOIN
Schedule ON Schedule.UserID = Users.UserID WHERE Users.TypeID = 5
Let's phrase this a little differently. A user is unavailable if the user has both AM and PM scheduled for the DAY column. Otherwise, the user is available.
Given that there are only two values in that column, the following query does the filtering you want:
SELECT u.UserID, u.Name
FROM Users u LEFT OUTER JOIN
Schedule s
ON s.UserID = u.UserID and
s.ScheduleDate = '2013-08-27'
WHERE u.TypeID = 5
GROUP BY u.UserID, u.Name
HAVING COUNT(distinct s.day) < 2;
If you know the values are never repeated, then you can change the having clause to:
HAVING COUNT(*) < 2;
This is a bit of a trick. When there is no match in the schedule table at all, the counts will return 0 (in the first case) or 1 (in the second case).
SELECT USERS.USERID,
USERS.NAME
FROM USERS
WHERE NOT EXISTS (SELECT SCHEDID
FROM SCHEDULE
WHERE SCHEDULE.USERID = USERS.USERID
AND DAY = 'AM')
AND NOT EXISTS (SELECT SCHEDID
FROM SCHEDULE
WHERE SCHEDULE.USERID = USERS.USERID
AND DAY = 'PM')