Recursive SQL Query, but not the usual kind of recursive - sql

I have a set of tables that organize a group of people into teams.
Users (ID int PK, etc.)
Teams (ID int PK, etc.)
UsersToTeams (ID int PK, UserID int FK, TeamID int FK, TeamSupervisor bit not null)
There is no Parent ID in the table because users can be on any number of teams, and teams can have any number of supervisors. A user may be on six teams but only supervise two of them, and one or both of those supervised teams may have other supervisors in it. So my hierarchy looks more like a web than a tree.
I recognize that a recursive query may result in a circular reference. Assume the software is handling that for the moment.
The company hierarchy is described by a Supervisor supervising a team of Users, a Manager supervising a team of Supervisors, etc. So it's hierarchical, but not in the usual way.
I need a query which, given a UserID, will return the IDs of the users he supervises, down infinite levels. How might such a query go?
Example
Users (ID, Name)
1 Archie
2 Betty
3 Chuck
4 Dilton
5 Eddie
6 Fannie
User 1 is a Manager (level 3). Users 2 and 3 are Supervisors (level 2). Users 4, 5, 6 are Users (level 1).
Teams (ID, Name)
1 Team Alpha
2 Team Bravo
3 Sup Team
UsersToTeams (ID INT PK, UserID INT FK, TeamID INT FK, isSupervisor BIT)
1 1 3 1 -- Archie supervises Sup Team
2 2 3 0 -- Betty is a member of Sup Team
3 3 3 0 -- Chuck is a member of Sup Team
4 2 1 1 -- Betty supervises team Alpha
5 4 1 0 -- Dilton is a member of team Alpha
6 5 1 0 -- Eddie is a member of team Alpha
7 3 2 1 -- Chuck supervises Team Bravo
8 6 2 0 -- Fannie is a member of Team Bravo
Archie is a Manager, supervising a team of supervisors.
Betty is a Supervisor, supervising a team of users.
Chuck is a Supervisor, supervising a team of users.
Betty and Chuck are also on Archie's team, but do not supervise it.
Therefore:
If I pass in UserID 5 (Eddie), I should get back only 5, because Eddie doesn't supervise anyone.
If I pass in UserID 3 (Chuck), I should get back 3 and 6, because Fannie is on a team that Chuck supervises.
If I pass in UserID 1 (Archie), I should get back all UserIDs described here, because Betty and Chuck are on Archie's team, and everyone else is on either Betty's team or Chuck's team.
Sorry, I tried that SQL fiddle link, but after 15 minutes of "Building Schema" I lost hope for it.

You can do this with a recursive CTE.
First, select the user himself, and then recursively select all the user he's immediately supervising over:
declare #userID int = 1;
with u as (
select id from users where id = #userID
union all
select lacky.userID from u supervisor
join usersToTeams supervising on supervising.userID = supervisor.id and isSupervisor = 1
join usersToTeams lacky on lacky.teamID = supervising.teamID and lacky.isSupervisor = 0
)
select * from u
Here's the fiddle: http://www.sqlfiddle.com/#!3/525e1/3

Related

List records from a chosen employee to the ultimate manager

Given the following Table (In Postgres):
EmployeeID
EmployeeName
ManagerID
1
David
NULL
2
Jessica
NULL
3
Gregg
1
4
Joe
3
5
Brandon
NULL
6
Leslie
4
7
Harry
6
8
Paul
NULL
9
Frank
5
Starting from Employee ID 7 (Harry) - how do I list only the records, bottom-up, in the hierarchy until I get to the ultimate manager (which should be David, in this case)? Should I use recursive CTE? If so, how does that query look?
Expected Output:
EmployeeID
EmployeeName
ManagerID
1
David
NULL
3
Gregg
1
4
Joe
3
6
Leslie
4
7
Harry
6
In the common table expression, you can recognize:
the base step, where you select the record relatively to employee no 7
the recursive step, where you match managers with currently retrieved employees.
The recursion stops when the INNER JOIN does not return any more extra record.
WITH RECURSIVE cte AS (
SELECT * FROM tab WHERE EmployeeID = 7
UNION ALL
SELECT mgr.*
FROM cte emp
INNER JOIN tab mgr
ON emp.ManagerID = mgr.EmployeeID
)
SELECT *
FROM cte
Check the demo here.

BigQuery count each item in array across table

I cannot quite find what I'm looking for, so here goes:
I'm looking for a way to get a count of the number of times an item occurs in an array across the entire table.
Imagine you have a table child_names with two columns - user_id and children
I know it's unusual to have two children with same name, but bear with me
.
user_id children
1 Bob, Jane, Bob
2 Jeff, Jane
3 Bob, Matt
4 Jane, John
I am looking for a result that would have two columns
Bob 3
Jane 3
Jeff 1
Matt 1
John 1
So far I have this
SELECT
ARRAY(
SELECT AS STRUCT child, `count`
FROM t.children child
LEFT JOIN (
SELECT AS STRUCT child, COUNT(1) `count`
FROM t.children child
GROUP BY child
) stats
USING(child)
) hashtag
FROM `child_names` t,
UNNEST(children)
But this gives me a count of how many children have that name per parent, not per table.
I get
Bob 2
Jane 1
Jeff 1
Jane 1
Bob 1
Matt 1
etc.
I hope that makes sense. Any help would be appreciated.
Use below
SELECT name, COUNT(*) cnt
FROM child_names,
UNNEST(children) name
GROUP BY name
if applied to sample data in your question - output is

Solving Logical Questions Using SQL

I am trying to solve a problem for a fun work exercise showing that SQL can be used to solve it. It is a puzzle that goes as follows:
Successfully navigating the waters during sea voyages is a challenging task. A captain’s most important decision is selecting the right crew for the voyage. A mix of different skill sets are required to sail the ship efficiently, navigate to the destination, and fish for food along the way.
Table 1 shows a list of crew members that are available for you to hire for the voyage. Each crew member demands a salary for the voyage and has different skill levels of Fishing, Sailing, and Navigation.
In order for your journey to be successful, you must have a cumulative skill of 15 or more in each of the three skill categories from all of your chosen crew members. You may choose as many crew members as you like.
Question: What is the minimum achievable cost for the voyage?"
I would say I am what I would consider an intermediate to advanced (depending on the situation) SQL user.
Not asking for an answer per-say but I have thought about the best way to solve and I was first thinking using a WHILE loop in some way. I have create a table to hold the data and added a 'salary_ranking' column (below). I am curious if anyone has any tips or suggestions on routes to go? I would like to use something I have never used before but also am trying to get to the most efficient answer.
Here is the data (I added the last column):
NAME FISHING SAILING NAVIGATION SALARY SALARY_RANK
---------- ----------- ----------- ----------- ----------- -----------
Amy 3 5 1 46000 3
Bill 1 2 5 43000 2
Carl 3 4 2 47000 4
Dan 4 3 1 36000 1
Eva 4 2 2 43000 2
Fred 1 3 4 55000 5
Greg 3 1 5 68000 8
Henry 5 4 2 64000 7
Ida 3 3 3 60000 6
(9 rows affected)
This is a CTE version, where I first create test data, then run a recursive query, using a MaxID to prevent it doing all the permutations.
declare #t table(Id int, NAME varchar(10), FISHING int, SAILING int, NAVIGATION int, SALARY int)
insert #t values (1,'Amy',3,5,1,46000)
,(2,'Bill',1,2,5,43000 )
,(3,'Carl',3,4,2,47000)
,(4,'Dan',4,3,1,36000)
,(5,'Eva',4,2,2,43000)
,(6,'Fred',1,3,4,55000)
,(7,'Greg',3,1,5,68000)
,(8,'Henry',5,4,2,64000)
,(9,'Ida',3,3,3,60000 )
;with cte as (
select convert(varchar(1000),name) as crew, fishing, sailing, navigation, salary, ID as MaxID from #t
union all
select convert(varchar(1000),cte.crew+', '+ t.name), cte.fishing+t.fishing, cte.sailing+t.sailing, cte.navigation+t.navigation, cte.salary+t.salary, t.ID
from #t t
join cte on t.ID>cte.MaxID
)
select top 1 crew,fishing,sailing,navigation,salary
from cte
where fishing>=15 and sailing>=15 and navigation>=15
order by salary
result is:
crew fishing sailing navigation salary
Amy, Bill, Carl, Greg, Henry 15 16 15 268000

SQL - Find duplicate children

I have a table containing meetings:
MeetID Description
-----------------------------------------------------
1 SQL Workshop
2 Cake Workshop
I have another table containing all participants in the meetings:
PartID MeetID Name Role
-----------------------------------------------------
1 1 Jan Coordinator
2 1 Peter Participant
3 1 Eva Participant
4 1 Michael Coordinator
5 2 Jan Coordinator
6 2 Peter Participant
I want to find is a list of all meetings that have 2 or more participants with Role = 'Coordinator'.
Eg. in the example above that would be the meeting with MeetID=1 and not 2.
I cannot for the life of me figure out how to do this, allthough I think it should be simple :-)
(I am using SQL Server 2012)
This is easy to do using group by and having:
select MeetId
from participants p
where Role = 'Coordinator'
group by MeetId
having count(*) >= 2;
Note: Role is a potential keyword/reserved word, so it is a bad choice for a column name.

Categories datatype

I want to check what hobbies a user has and I want to insert it to a database.
I was thinking about making a column Hobbies and to insert the data in a string type in that form : {"Hobby1", "Hobby2", "Hobby3"}
But then the code to filter out the hobbies would be big, is there a more efficient way?
Create a Hobbies table and insert hobbies into that such as Golf, Photography etc.
Then create a UserHobby table to associate a User with a Hobby such that a User can have multiple hobbies.
EG
UserID FirstName
1 Bob
2 John
HobbyID HobbyName
1 Golf
2 Photography
UserHobbyID UserID HobbyID
1 1 1
2 1 2
3 2 2