sql find all instances where all are part of distinct group - sql

I'm sure a similar question to this has already been asked and answered, but I haven't been able to find anything in search, please be gentle.
I would like to know all the names of faculty members in a database who teach in every room of a building. The tables are very bare, but they are:
class:
+--------+---------+------+------+
| cname | meetsat | room | fid |
+--------+---------+------+------+
| class | 8 | R128 | 5 |
| class2 | 9 | R129 | 6 |
| class3 | 9 | R128 | 5 |
+--------+---------+------+------+
faculty:
+-----+---------------+--------+
| fid | fname | deptid |
+-----+---------------+--------+
| 5 | i.teach | 999 |
| 6 | other guy | 998 |
| 8 | another woman | 997 |
+-----+---------------+--------+
Through discussion with other users so far, I have:
(SELECT f.fname
FROM faculty f, class c
WHERE f.fid = c.fid)
UNION
(select c.fid
from class c
group by c.fid
having count(distinct room) = (select count(distinct c2.room) from class
c2));
current output:
+-----------+
| fname |
+-----------+
| i.teach |
| other guy |
+-----------+
desired output should be:
+---------+
| fname |
+---------+
| i.teach |
+---------+
I think I only need to join correctly. The course materials I have are extremely bare-bones and don't offer much in concept instruction, so I don't know who to apply them in different situations.

Here's a query that may do what you require, implementing your algorithm of comparing counts. It is an alternative to the HAVING posted by Gordon
SELECT * FROM
(SELECT count(distinct room) as countAllRooms FROM class) ar
INNER JOIN
(SELECT c.fid, count(distinct c.room) as countRoomsPerTeacher FROM class c GROUP BY c.fid) rpt
ON
rpt.countRoomsPerTeacher = ar.countAllRooms
INNER JOIN
faculty f
ON
f.fid = rpt.fid
In relation to your query on Gordon's answer, the safest way to join the faculty table:
Select * from faculty inner join
(
select c.fid
from class C
group by c.fid
having count(distinct room) = (select count(distinct c2.room) from class c2)
) ff
on ff.fid = faculty.fid
I wouldn't normally format an sql like this but I've done this deliberately to show the bits I added and which bits were Gordon's
You should avoid trying to join the he faculty table into the inner query that does the grouping as it will force you to add more columns to your select list, which forces you to add more to your group by, which breaks your counting,, better to consider Gordon's query a "faculty finder" that runs in isolation as a sub query and is joined later

You are looking for having:
select c.fid
from class c
group by c.fid
having count(distinct c.room) = (select count(distinct c2.room) from class c2);
Getting the name is just a matter of joining in the faculty table.

What about inner join or I didn’t understand your question.
Select f.name from faculty f inner join class c on f.fid=c.fid

Related

Include zero counts when grouping by multiple columns and setting filters

I have a table (tbl) containing category (2 categories), impact (3 impacts), company name and date for example:
category | impact | company | date | number
---------+----------+---------+-----------|
Animal | Critical | A | 12/31/1999|1
Book | Critical | B | 12/31/2000|2
Animal | Minor | C | 12/31/2001|3
Book | Minor | D | 12/31/2002|4
Animal | Medium | E | 1/1/2003 |5
I want to get the count of records for each category and impact and be able to add rows with zero count and also be able to filter by company and date.
In the example result set below, the count result is 1 for category = Animal and company = A. The rest is 0 records and only the Critical and Medium impacts appear
category | impact | count
---------+----------+-------
Animal | Critical | 1
Animal | Medium | 0
I've looked at the responses to similar questions by using joins however, adding a WHERE clause doesn't include the zero records.
I also tried doing outer joins but it doesn't produce desired output. For example
select a.impact, b.category, ISNULL(count(b.impact), 0) from tbl a
left outer join tbl b
on b.number = a.number
and (a.category = 'Animal' and a.company in ('A'))
group by a.impact, b.category
produces
impact | category | count
---------+------------+--------
Medium | NULL | 0
Medium | Animal | 1
Critical | NULL | 0
Minor | NULL | 0
but the desired output should be
category | impact | count
---------+----------+-------
Animal | Critical | 1
Animal | Medium | 0
Animal | Minor | 0
Any help will be appreciated. Answers to associated questions don't have filtering so I will appreciate if someone can help with a query to produce desired output.
You need a master table with all the possible combinations of Categories and Impacts for this. Then Left join your table with the master and do the aggregation. Something like below
;WITH CAT
AS
(
SELECT
category
FROM Tbl
GROUP BY category
),
IMP
AS
(
SELECT
Impact
FROM Tbl
GROUP BY Impact
),MST
AS
(
SELECT
*
FROM CAT
CROSS JOIN IMP
)
SELECT
MST.category,
MST.Impact,
COUNT(T.Number)
FROM MST
LEFT JOIN Tbl T
ON MST.category = T.category
AND MST.Impact = T.Impact
AND T.Company = 'A'
WHERE MST.Category = 'Animal' GROUP BY MST.category,
MST.Impact

SQL Server avoid repeat same joins

I´m doing the query below where I´m repeating the same joins multiple times, there is a better way to do it? (SQL Server Azure)
Ex.
Table: [Customer]
[Id_Customer] | [CustomerName]
1 | Tomy
...
Table: [Store]
[Id_Store] | [StoreName]
1 | SuperMarket
2 | BestPrice
...
Table: [SalesFrutes]
[Id_SalesFrutes] | [FruteName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Orange | 1 | 1
...
Table: [SalesVegetable]
[Id_SalesVegetable] | [VegetableName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Pea | 1 | 2
...
Select * From [Customer] as C
left join [SalesFrutes] as SF on SF.[Fk_Id_Customer] = C.[Id_Customer]
left join [SalesVegetable] as SV on SV.[Fk_Id_Customer] = C.[Id_Customer]
left join [Store] as S1 on S1.[Id_Store] = SF.[Fk_Id_Store]
left join [Store] as S2 on S1.[Id_Store] = SV.[Fk_Id_Store]
In my real case, I have many [Sales...] to Join with [Customer] and many other tables similar to [Store] to join to each [Sales...]. So it starts to scale a lot the number on joins repeating. There is a better way to do it?
Bonus question: I do like also to have FruteName, VegetableName, StoreName, and each Food table name under the same column.
The Expected Result is:
[CustomerName] | [FoodName] | [SalesTableName] | [StoreName]
Tomy | Orange | SalesFrute | SuperMarket
Tomy | Pea | SalesVegetable | BestPrice
...
Thank you!!
So based on the information provided, I would have suggested the below, to use a cte to "fix" the data model and make writing your query easier.
Since you say your real-world scenario is different to the info provided it might not work for you, but could still be applicable if you have say 80% shared columns, you can just use placeholder/null values where relevant for unioning the data sets and still minimise the number of joins eg to your store table.
with allSales as (
select Id_SalesFrutes as Id, FruitName as FoodName, 'Fruit' as SaleType, Fk_Id_customer as Id_customer, Fk_Id_Store as Id_Store
from SalesFruits
union all
select Id_SalesVegetable, VegetableName, 'Vegetable', Fk_Id_customer, Fk_Id_Store
from SalesVegetable
union all... etc
)
select c.CustomerName, s.FoodName, s.SaleType, st.StoreName
from Customer c
join allSales s on s.Id_customer=c.Id_customer
join Store st on st.Id_Store=s.Id_Store

Condition expected - can't figure out why

I have the following query but it's not working. I want to see only one studentname because dated is unique.
SELECT r.classteacher,
r.studentname,
r.reggroup,
r.yeargroup,
rrl.cdated
FROM [dbo].[Reports] AS r
LEFT OUTER JOIN reportsreadlog AS rrl
ON r.rguid = rrl.creportguid
WHERE
(
SELECT TOP(1) r.studentname
FROM [dbo].[Reports] AS r
LEFT OUTER JOIN reportsreadlog AS rrl
ON r.rguid = rrl.creportguid
WHERE r.periodguid = '4390dc5f-eb21-4673-83f0-e7f973524916'
)
AND r.periodguid = '4390dc5f-eb21-4673-83f0-e7f973524916'
GROUP BY r.classteacher,
r.studentname,
r.reggroup,
r.yeargroup,
rrl.cdated
ORDER BY r.reggroup
Current result when I fix the error:
classteacher | studentname | reggroup | yeargroup | cdate
-------------+-------------+----------+-----------+------
Teacher 1 | Student 1 | class | year | dated
Teacher 1 | Student 1 | class | year | dated
Teacher 1 | Student 1 | class | year | dated
Desired result:
classteacher | studentname | reggroup | yeargroup | cdate
-------------+-------------+----------+-----------+------
Teacher 1 | Student 1 | class | year | dated
Teacher 1 | Student 2 | class | year | dated
Teacher 1 | Student 3 | class | year | dated
Without table definitions and sample/test data, its kind of hard to answer your question. So, please update your post to add them to assist further.
Meanwhile, please try below:
SELECT distinct r.classteacher,
r.studentname,
r.reggroup,
r.yeargroup,
rrl.cdated
FROM [dbo].[Reports] AS r
LEFT OUTER JOIN reportsreadlog AS rrl
ON r.rguid = rrl.creportguid
WHERE r.periodguid = '4390dc5f-eb21-4673-83f0-e7f973524916' --Based on my understanding you don't need other subquery condition here.
-- GROUP BY r.classteacher,
-- r.studentname,
-- r.reggroup,
-- r.yeargroup,
-- rrl.cdated
ORDER BY r.reggroup
Thank you all for helping out. In the end i gave up and did it the ugly way.
Ran the query twice and combined the results
Once and rrl.cdated is null and after that and rrl.cdated is not null

How can I find all columns A whose subcategories B are all related to the same column C?

I'm trying to better understand relational algebra and am having trouble solving the following type of question:
Suppose there is a column A (Department), a column B (Employees) and a column C (Managers). How can I find all of the departments who only have one manager for all of their employees? An example is provided below:
Department | Employees | Managers
-------------+-------------+----------
A | John | Bob
A | Sue | Sam
B | Jim | Don
B | Alex | Don
C | Jason | Xie
C | Greg | Xie
In this table, the result I should get are all tuples containing departments B and C because all of their employees are managed by the same person (Don and Xie respectively). Department A however, would not be returned because it's employees have multiple managers.
Any help or pointers would be appreciated.
Such problems usually call for a self-join.
Joining the relation onto itself on Department, then filtering out the tuples where the Managers are equal would yield us all the unwanted tuples, which we can just subtract from the original relations.
Here's how I'd do it:
First we make a copy of table T, and call it T2, then take a cross product of T and T2. From the result we select all the rows where T1.Manager /= T2.Manager but T1.Department=T2.Department, yielding us these tuples:
T1.Department | T1.Employees| T1.Managers | T2.Managers | T2.Employees | T2.Department
--------------+-------------+-------------+-------------+--------------+--------------
A | John | Bob | Sam | Sue | A
A | Sue | Sam | Bob | John | A
Departments A and B aren't present because their T1.Manager always equals T2.Manager.
Then we just subtract this result the original set to get the answer.
If your RDBMS supports common table expressions:
with C as (
select department, manager, count(*) as cnt
from A
group by department, manager
),
B as (
select department, count(*) as cnt
from A group by department
)
select A.*
from A
join C on A.department = C.department
join B on A.department = B.department
where B.cnt = C.cnt;

Concatenating multiple rows that differ in only one column

I'm writing an app and I have the following tables in SQLite:
course:
_id | name | a | b
university:
_id | name | c | d
course_university:
course_id | university_id
Course_university links the courses with the universities that offer them. It's a many-to-many relationship. I need a request that would give me the following
course._id | course.name | course.a | university.name | university.c
The query I thought would work was
SELECT c._id, c.name, c.a, u.name, u.c
FROM course AS c, university AS u, course_university AS cu
WHERE c._id=cu.course_id AND u._id=cu.university_id
The problem is that if there is a course offered by more than one university, the above query will show it twice, the only difference being in the university column. Is there a way to concatenate the university names for once course, so instead of getting
20 | Calculus | 23 | Stanford | 5 |
20 | Calculus | 23 | Harvard | 5 |
I'd get
20 | Calculus | 23 | Stanford & Harvard | 5 |
In my case there might be more than 2 universities working together on one course, so if it accommodates for concatenating three rows then great. This is my first time dealing with SQL databases, so I'm not that aware of any more advanced methodology to solve this.
Here is an example of how you use group_concat():
SELECT c._id, c.name, c.a, group_concat(u.name, ' & ') as universities, u.c
FROM course_university cu join
course c
on c._id = cu.course_id join
university AS u,
on u._id = cu.university_id
group by c._id, c.name, c.a, u.c;
I also changed the query syntax to use explicit, ANSI standard join syntax.