Many to Many with multiple Ands - sql

I am trying a simple (or not) query to get Users that exist in 2 Departments.
Struct:
User
ID
Name
UserDepartment
ID
IDUser
IDDepartment
Department
ID
Name
So i want users from DepartmentA and DepartmentB (
Is imposible to do:
Select * from User as US
left join UserDepartment as DP on User.ID = UserDepartment.IDUser
where DP.IDDepartment = 1 and DP.IDDepartment = 2
Zero results...
An query to resolve this is sothing like:
select * from UserDepartment
where IDDepartment in (1,2)
group by IDUser
having COUNT(*)=2
But is this the only solution? there are other easy queries out there?

To get all users that belong in both 1 and 2 departments:
SELECT *
FROM User
WHERE User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 1)
AND User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 2)
The results are essentially the same as the latter query you have, except it specifically looks for people who are in both "1" and "2", rather than anybody who has 2 or more department records that belong to "1" or "2" (doesn't include people with two "1" records but no "2" records).
If there's information from the userdepartment table you want included in the results, just change it to:
SELECT *
FROM User US
INNER JOIN (SELECT * FROM UserDepartment WHERE IDDepartment IN(1,2)) DP ON US.ID = DP.UserID
WHERE User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 1)
AND User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 2)

This seems like a job for INTERSECT. You can get the list of user id's you want by doing something like:
SELECT IDUser FROM UserDepartment WHERE IDDepartment = 1
INTERSECT
SELECT IDUser FROM UserDepartment WHERE IDDepartment = 2

/*** If only users for dept 1 and 2 need to be shown the use this query ****/
Select * from #User u
inner join
(
Select d1.UserID from #UserDepartment d1
where d1.Department = 1
intersect
Select d2.UserID from #UserDepartment d2
where d2.Department = 2
except
Select d2.UserID from #UserDepartment d2
where d2.Department not in (1,2)
)t on u.ID = t.UserID
/***If the users that belong to dept 1 and 2 and can exist in
other depts then use this one**/
Select * from #User u
inner join
(
Select d1.UserID from #UserDepartment d1
where d1.Department = 1
intersect
Select d2.UserID from #UserDepartment d2
where d2.Department = 2
)t on u.ID = t.UserID
/**To test use the following commented code**/
/*Select * into #User from
(
Select 1 as ID, 'A' as Name
union
Select 2,'B'
union
Select 3,'C'
union
Select 4,'D'
union
Select 5,'E'
union
Select 6,'F'
union
Select 7,'G'
union
Select 8,'H'
union
Select 9,'I'
union
Select 10,'G'
)t
Select * into #Department from
(
Select 1 as ID, 'D1' as Name
union
Select 2,'D2'
union
Select 3,'D3'
union
Select 4,'D4'
)t2
Select * into #UserDepartment from
(
Select 1 AS ID ,1 AS UserID ,1 AS Department
union
Select 2,1,2
union
Select 3,1,3
union
Select 4,2,1
union
Select 5,3,2
union
Select 6,4,1
union
Select 7,4,2
)t3
*/

Related

SQL Select for Users only in certain List of Groups

I need to select a group of users that are in certain user_groups, but only in those user_groups.
User
1
2
3
Group
a
b
c
UserToGroup
1, a
1, b
1, c
2, a
2, c
3, b
3, c
User must only be in Groups a and c
Result
2, a
2, c
Group and User are both scaling tables, so excluding unwanted groups in the query is not an option.
EDIT: I modified the query to include other data from the user table.
If you need to get other data from user and/or group, you can use a simple JOIN with NOT EXISTS to eliminate records with something other than your select list. It should optimize pretty well. The HAVING count(*) relies on there not being more than one linking record in the usertorgroup for a user and group. If either is a concern, the query can be filtered further to eliminate those duplicates. It also needs to be passed how many groups are being searched. How are you passing this list of values to the query?
SELECT u.uid AS userid, u.otherstuff
FROM usr u
INNER JOIN usertogroup utg ON u.uid = utg.uid
INNER JOIN grp g ON utg.gid = g.gid
AND g.gid IN ('a','c')
WHERE NOT EXISTS (
SELECT 1
FROM usertogroup utg2
WHERE utg2.gid NOT IN ('a','c')
AND utg2.uid = u.uid
)
GROUP BY u.uid, u.otherstuff
HAVING count(*) = 2 /* # of items in list to search. */
Demo:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f51dbd8a6013d9ec94120cf1ec512735
http://sqlfiddle.com/#!9/96e2a7/1/0
Use:
SELECT *
FROM UserToGroup
WHERE `user` IN (
SELECT `user`
FROM UserToGroup
GROUP BY `user`
HAVING count(distinct `group`)
= count(distinct CASE WHEN `group` IN ('a','c') THEN `group` END )
)
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=19bfcfa320ca5e2afbf48a1bbb09a1a1
I need distinct users. I have reduced the columns of user to easen the
example.
To get data from Users use this query:
SELECT *
FROM Users
WHERE `user` IN (
SELECT `user`
FROM UserToGroup
GROUP BY `user`
HAVING count(distinct `group`)
= count(distinct CASE WHEN `group` IN ('a','c') THEN `group` END )
)
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=7a75d024ce0c871bca6ff3062ca1bc0f
If you want users that are only in A and C, then you can do this:
SELECT
MIN(USER_ID) AS USER_ID
FROM
UserToGroup
HAVING
MIN(USER_GROUP) = 'a' AND MAX(USER_GROUP) = 'c'
If they are in A and C and D, then that query won't work for you.
You could also try
SELECT
USER_ID
FROM
UserToGroup
WHERE USER_GROUP = 'a'
AND USER_ID IN (SELECT USER_ID FROM UserToGroup WHERE USER_GROUP = 'c')
This query gets everyone that is in 'a', and also looks in a subquery against the same table for users that are in 'c'.
You can try conditional aggregation in a HAVING clause, that checks for the counts of 'a' and 'c' to be exactly 1 and all others to be 0.
Assuming the column names of usertogroup are user and group.
SELECT ug1.user,
ug1.group
FROM usertogroup ug1
INNER JOIN (SELECT ug2.user
FROM usertogroup ug2
GROUP BY ug2.user
HAVING count(CASE
WHEN ug2.group = 'a') THEN
1
END) = 1
AND count(CASE
WHEN ug2.group = 'c') THEN
1
END) = 1
AND count(CASE
WHEN ug2.group NOT IN ('a', 'c') THEN
1
END) = 0) x
ON x.user = ug1.user;
I hope this is what you want :
SELECT
CASE WHEN GROUPS IN ('a','c')
THEN
USERS||'-'||GROUPS
ELSE
'USER IN SOME OTHER GROUP'
END CASE
FROM TABLE order by Groups ;
First get the users that belong to exactly 2 groups and then apply the condition that these groups are a and c:
select u.* from UserToGroup as u
inner join (
select userid from UserToGroup
group by userid
having count(*) = 2
) as t
on
t.userid = u.userid
and
u.groupid IN ('a', 'c')
and
not exists (
select 1
from UserToGroup as uu
where
uu.userid = t.userid
and
uu.groupid NOT IN ('a', 'c')
)
See the demo

Select hierarchy from table

I have a table ServiceItem that has Id, ParentId and some properties like Name, Description and so on. Max level of hierarchy is 2. I need a query that selects rows with some condition, for example Name = '123' and its parent row to get smth like:
Id ParentId Name
1 NULL '12'
2 1 '123'
I have tried this:
SELECT
*
FROM ServiceItem si
WHERE si.Name = '123'
OR EXISTS (
SELECT
*
FROM ServiceItem syst
JOIN ServiceItem si2
ON si2.ParentId = syst.Id
WHERE syst.Id = si.ParentId
AND si2.Name = '123'
)
But it returns parent and all of it's children. Is there any chance to do it with one query? I'm using T-SQL to do it.
It's differs from this question because i need to get a bunch of rows, not only Id by path and conditions in my query could be different.
You can use a Common Table Expression with recursion:
WITH cte AS
(
SELECT *
FROM ServiceItem
WHERE Name = '123'
UNION ALL
SELECT *
FROM ServiceItem si
INNER JOIN cte
ON cte.ParentId = si.Id
)
SELECT * FROM cte
For a more in-depth example, see this Q&A
WITH cte AS
(
SELECT *
FROM ServiceItem
WHERE Name = '123'
UNION ALL
SELECT *
FROM ServiceItem si
INNER JOIN cte
ON cte.ParentId = si.Id
)
SELECT * FROM cte
It's a good query, but I also found this one:
SELECT
*
FROM ServiceItem si
WHERE si.Name = '123'
OR EXISTS (
SELECT
*
FROM ServiceItem si2
WHERE si2.Name = '123'
and si2.ParentId = si.Id
)

How to get amount of records of a specific ID in a database?

I want to check which users have the most records in a database. So every user has a specific Id, and that Id is used as a reference in a few tables.
There are a few tables that contain a column UserId, like the table Exams, Answers, Questions, Classes, etc. Is it possible to count all the records with a specific UserId in all those tables?
;with cte as (
select rowsCount = count(*) from A where UserId = 1
union all
select rowsCount = count(*) from B where UserId = 1
union all
select rowsCount = count(*) from C where UserId = 1
)
select sum(rowsCount) from cte
I would do it in this way:
With AllRecords AS
(
SELECT TableName = 'Exams'
FROM dbo.Exams
WHERE UserId = #YouruserID
UNION ALL
SELECT TableName = 'Answers'
FROM dbo.Answers
WHERE UserId = #YouruserID
UNION ALL
SELECT TableName = 'Questions'
FROM dbo.Questions
WHERE UserId = #YouruserID
UNION ALL
SELECT TableName = 'Classes'
FROM dbo.Classes
WHERE UserId = #YouruserID
)
SELECT COUNT(*) FROM AllRecords
The table-name is not needed if you just want the count, it's just for the case that you want to know the source.
Try like this,
SELECT Count(E.ID) + Count(A.ID) + Count(Q.ID) + Count(C.ID) AS SumofAllCount
FROM dbo.Exams E
LEFT JOIN dbo.Answers A ON A.UserId = E.UserId
LEFT JOIN dbo.Questions Q ON Q.UserId = E.UserId
LEFT JOIN dbo.Classes C ON C.UserId = E.UserId
WHERE E.UserId = #YouruserID
GROUP BY E.UserId

How to merge these queries into 1 using subquery

Select * from HotelPerson
Where RoomID IN (select ID from HotelRoom Where BookingID = 36 )
Select * from HotelCancelationPolicy
Where RoomID IN (select ID from HotelRoom Where BookingID = 36 )
How can I merge these both queries into 1 query ?
This will give you all the columns from both tables in one table.
SELECT *
FROM HotelPerson A, HotelCancelationPolicy B
WHERE A.RoomID = B.RoomID
AND A.RoomID IN (SELECT ID FROM HotelRoom WHERE BookingID = 36)
Use UNION to Get Distinct elements or UNION ALL for all rows from both tables
Select * from HotelPerson
Where RoomID IN (select ID from HotelRoom Where BookingID = 36 )
UNION ALL
Select * from HotelCancelationPolicy
Where RoomID IN (select ID from HotelRoom Where BookingID = 36 )
I guess you want to join the two tables:
select *
from HotelPerson hp
inner join HotelCancelationPolicy hcp
on hp.RoomId = hcp.RoomId
where hp.RoomID IN (select ID
from HotelRoom
where BookingID = 36 )

Need help on SQL join

I have 2 tables as below:
select 1 as id,
'A' as place
into #places
UNION
select 2 as id,
'B' as place
UNION
select 3 as id,
'C' as place
UNION
select 4 as id,
'D' as place
select 'x' as name,
2 as start,
3 as endd
into #travel
UNION
select 'y' as name,
4 as start,
1 as endd
UNION
select 'z' as name,
1 as start,
3 as endd
select * from #places
select * from #travel
------------------------------
Now I want to get name/start place/end place using joins?
SELECT
t.name,
p1.place as start_place,
p2.place as end_place
FROM
travel t
JOIN places p1 on t.start = p1.id
JOIN places p2 on t.endd = p2.id
select #travel.*, pstart.place as start, pend.place as end
from #travel inner join #places as pstart on pstart.id = #travel.start
inner join #places as pend on pend.id = #travel.end