WHERE condition for Intersection table - sql

Let's assume I have the following intersection table MemberClub:
MemberID | ClubId
1 | 2
2 | 1
2 | 2
2 | 3
I'd like to get all members which are in the clubs with ID 1 and 2. So the result should be member with ID 2.
How would my where statement look like? I tried different variations but I'm not sure which one to choose.
Expected output should be:
MemberID | ClubId
2 | 1
2 | 2
I need a flexible version which works for a flexible number of clubs (it could be that I want all members being in club with just ID 1. Or all members being in club with ID 2, 4 and 6).

General GROUP BY with HAVING COUNT(DISTINCT) solution:
select MemberID
from MemberClub
WHERE ClubId IN (1,2)
GROUP BY MemberID
HAVING COUNT(DISTINCT ClubId) = 2
I.e. make sure there are two different ClubId's for a user.
Alternatively, make sure there are different ClubId's for a user (works just in the two club case):
select MemberID
from MemberClub
WHERE ClubId IN (1,2)
GROUP BY MemberID
HAVING max(ClubId) <> min(ClubId)
Or skip the GROUP BY, do a self JOIN instead:
select distinct m1.MemberID
from MemberClub m1
join MemberClub m2 on m1.MemberID = m2.MemberID
where m1.ClubId = 1
and m2.ClubId = 2
To return members who are in all clubs:
select MemberID
from MemberClub
GROUP BY MemberID
HAVING COUNT(DISTINCT ClubId) = (select count(distinct ClubId) from MemberClub)

Related

Select count of total records and also distinct records

I have a table such as this:
PalmId | UserId | CreatedDate
1 | 1 | 2018-03-08 14:18:27.077
1 | 2 | 2018-03-08 14:18:27.077
1 | 3 | 2018-03-08 14:18:27.077
1 | 1 | 2018-03-08 14:18:27.077
I wish to know how many dates were created for Palm 1 and I also wish to know how many users have created those dates for Palm 1. So the outcome for first is 4 and outcome for second is 3
I am wondering if I can do that in a single query as oppose to having to do a subquery and a join on itself as in example below.
SELECT MT.[PalmId], COUNT(*) AS TotalDates, T1.[TotalUsers]
FROM [MyTable] MT
LEFT OUTER JOIN (
SELECT MT2.[PalmId], COUNT(*) AS TotalUsers
FROM [MyTable] MT2
GROUP BY MT2.[UserId]
) T1 ON T1.[PalmId] = MT.[PalmId]
GROUP BY MT.[PalmId], T1.[TotalUsers]
According to first table you could do something like this:
select count(distinct uerid) as N_Users,
count(created_date) as created_date, -- if you use count(*) you consider also rows with 'NULL'
palmid
from your_table
group by palmid
If you want "4" and "3", then I think you want:
SELECT MT.PalmId, COUNT(*) AS NumRows, COUNT(DISTINCT mt.UserId) as NumUsers
FROM MyTable MT
GROUP BY MT.PalmId

Selecting objects that are associated with similar datasets

I'm trying to select all company rows from a [Company] table that share with at least one other company, the same number of employees (from an [Employee] table that has a CompanyId column), where each group of respective employees share the same set of LocationIds (a column in the [Employee] table) and in the same proportion.
So, for instance, two companies with three employees each that have the locationIds 1,2, and 2, would be selected by this query.
[Employee]
EmployeeId | CompanyId | LocationId |
========================================
1 | 1 | 1
2 | 1 | 2
3 | 1 | 2
4 | 2 | 1
5 | 2 | 2
6 | 2 | 2
7 | 3 | 3
[Company]
CompanyId |
============
1 |
2 |
3 |
Returns the CompanyIds:
======================
1
2
CompanyIds 1 and 2 are selected because they share in common with at least one other company: 1. the number of employees (3 employees); and 2. the number/proportion of LocationIds associated with those employees (1 employee has LocationId 1 and 2 employees have LocationId 2).
So far I think I want to use a HAVING COUNT(?) > 1 statement, but I'm having trouble working out the details. Does anyone have any suggestions?
This is ugly, but the only way I can think of to do it:
;with CTE as (
select c.Id,
(
select e.Location, count(e.Id) [EmployeeCount]
from Employee e
where e.IdCompany=c.Id
group by e.Location
order by e.Location
for xml auto
) LocationEmployeeData
from Company c
)
select c.Id
from Company c
join (
select x.LocationEmployeeData, count(x.Id) [CompanyCount]
from CTE x
group by x.LocationEmployeeData
having count(x.Id) >= 2
) y on y.LocationEmployeeData = (select LocationEmployeeData from CTE where Id = c.Id)
See fiddle: http://www.sqlfiddle.com/#!6/6bc16/5
It works by encoding the Employee count per Location data (multiple rows) into an xml string for each Company.
The CTE code on its own:
select c.Id,
(
select e.Location, count(e.Id) [EmployeeCount]
from Employee e
where e.IdCompany=c.Id
group by e.Location
order by e.Location
for xml auto
) LocationEmployeeData
from Company c
Produces data like:
Id LocationEmployeeData
1 <e Location="1" EmployeeCount="2"/><e Location="2" EmployeeCount="1"/>
2 <e Location="1" EmployeeCount="2"/><e Location="2" EmployeeCount="1"/>
3 <e Location="3" EmployeeCount="1"/>
Then it compares companies based on this string (rather than trying to ascertain whether multiple rows match, etc).
An alternative solution could look like this. However it also requires performance testing in advance (I don't feel quite confident with <> type join).
with List as
(
select
IdCompany,
Location,
row_number() over (partition by IdCompany order by Location) as RowId,
count(1) over (partition by IdCompany) as LocCount
from
Employee
)
select
A.IdCompany
from List as A
inner join List as B on A.IdCompany <> B.IdCompany
and A.RowID = B.RowID
and A.LocCount = B.LocCount
group by
A.IdCompany, A.LocCount
having
sum(case when A.Location = B.Location then 1 else 0 end) = A.LocCount
Related fiddle: http://sqlfiddle.com/#!6/d9f2e/1

TSQL - Query for multiple types in multiple rows

I have a table where each address has different types, for each type a row. How to find the addresses where for the exact types i need?
Eg.
ID TypID Street
1 1 Street 1
1 2 Street 1
2 2 Street 2
3 1 Street 3
3 2 Street 3
In the above i need to find addresses which has type 1 and 2. That query result should be adresses with id 1 and 3.
Group by the id and then count the different typeids in the having clause
select id from your_table
where typeid in (1,2)
group by id
having count(distinct typeid) = 2
You can use INTERSECT for this
select id
from tbl
where typid = 1
intersect
select id
from tbl
where typid = 2
although it won't work in mysql if that happens to be the database you're using.
This can be done with a inner join like the below
select y2.*
from <your_table> Y1
JOIN <your_table> Y2
ON Y1.ID = Y2.ID
AND Y1.Type_id in (1,2)
AND Y2.ID in (1,3)

(SQL) Match users belong to which group given user_id[]

user table
ID | name
1 | ada
2 | bob
3 | tom
group Table
ID | name
1 | group A
2 | group B
3 | group C
user_group Table
user_id | group_id
1 | 1
2 | 1
1 | 2
2 | 2
3 | 2
1 | 3
3 | 3
Given group of user ids : [1, 2, 3]
How to query the group that all users in the above list belongs to? (in this case: Group B)
To get all groups that contain exactly the specified users (i.e. all specified users and no other users)
DECLARE #numUsers int = 3
SELECT ug.group_id
--The Max doesn't really do anything here because all
--groups with the same group id have the same name. The
--max is just used so we can select the group name eventhough
--we aren't aggregating across group names
, MAX(g.name) AS name
FROM user_group ug
--Filter to only groups with three users
JOIN (SELECT group_id FROM user_group GROUP BY group_id HAVING COUNT(*) = #numUsers) ug2
ON ug.group_id = ug2.group_id
JOIN [group] g
ON ug.group_id = g.ID
WHERE user_id IN (1, 2, 3)
GROUP BY ug.group_id
--The distinct is only necessary if user_group
--isn't keyed by group_id, user_id
HAVING COUNT(DISTINCT user_id) = #numUsers
To get groups that contain all specified users:
DECLARE #numUsers int = 3
SELECT ug.group_id
--The Max doesn't really do anything here because all
--groups with the same group id have the same name. The
--max is just used so we can select the group name eventhough
--we aren't aggregating across group names
, MAX(g.name) AS name
FROM user_group ug
JOIN [group] g
ON ug.group_id = g.ID
WHERE user_id IN (1, 2, 3)
GROUP BY ug.group_id
--The distinct is only necessary if user_group
--isn't keyed by group_id, user_id
HAVING COUNT(DISTINCT user_id) = 3
SQL Fiddle: http://sqlfiddle.com/#!6/0e968/3
Try This:
Select t2.name
FROM
(Select group_id
From
user_group
Group by group_id
Having Count(user_id) = (Select Count(*) FROM User_Table)) AS T1
INNER JOIN
Group_Table AS T2
ON T1.group_id = T2.ID
See Fiddle: http://sqlfiddle.com/#!2/fa7250/4
Select UserID,count(*)
From UserGroupTable
group by UserID
This will give a count of 3 where the UserID/GroupID is unique (as zerkms pointed out)
SELECT name FROM group_tbl WHERE id IN (SELECT g_id FROM user_grp GROUP BY g_id HAVING Count(u_id)=(SELECT Count(id) FROM user_tbl));

SQL: Select from many-many through, joined on conditions in through table

Can't get my head around this...
I have 3 tables like this:
Computers
---------
Id
Name
ComputerLogins
--------------
Computer_Id
User_Id
NumberOfLogins
Users
-----
Id
Name
Computers "have and belong to many" Users "through" ComputerLogins.
Sample data:
Computers: Id Name
1 "Alpha"
2 "Beta"
3 "Gamma"
Users: Id Name
1 "Joe"
2 "Fred"
ComputerLogins: Computer_Id User_Id NumberOfLogins
1 1 5
1 2 12
2 1 10
2 2 6
3 1 2
3 2 4
I'm trying to construct a view that will output one row for each record in Computers, and join a Users row through MAX(NumberOfLogins) in ComputerLogins.
Desired output:
Computer_Id User_Id NumberOfLogins
1 2 12
2 1 10
3 2 4
Can you suggest a view query that will produce the desired output?
Thanks!
SELECT
CL.*, U.* --change this as needed
FROM
(
SELECT
Computer_ID, MAX(NumberOfLogins) AS NumberOfLogins
FROM
ComputerLogins
GROUP BY
Computer_ID
) maxC
JOIN
ComputerLogins CL On maxC.Computer_ID = CL.Computer_ID AND maxC.NumberOfLogins = CL.NumberOfLogins
JOIN
Users U On CL.User_ID = U.ID
Wrap in a view etc
Use:
CREATE VIEW your_view AS
SELECT c.id AS computer_id,
u.id AS user_id,
COUNT(*) AS NumberOfLogins
FROM COMPUTERS c
JOIN COMPUTERLOGINS cl ON cl.computer_id = c.id
JOIN USERS u ON u.id = cl.user_id
GROUP BY c.id, u.id