SQL query issue with achieving an encompasses all effect - sql

Below is the query I have been working on for this question.
Find the names of the companies which have employees residing in every city where employees of Mutual of Omaha live.
This means that if Mutual has employees in the cities Omaha, Lincoln, and Denver that the only company names it should return is a company that has employees in all 3 of those cities. This should also return Mutual.
The below query returns the company which has an employee in any of those three cities. The lastname is there for me to manually check which employees it is counting.
SELECT COMPANY_NAME, e1.lastname
FROM EMPLOYEE E1,WORKS W1
WHERE E1.CITY IN (SELECT CITY
FROM EMPLOYEE E2,WORKS W2
WHERE E2.firstname = W2.firstname
AND E2.lastname = W2.lastname
AND W2.COMPANY_NAME= 'Mutual of Omaha')
AND E1.firstname = W1.firstname
AND E1.lastname = W1.lastname;
I realized I didn't put the tables down so here they are
employee (Lastname, FirstName, MidInitial, gender, street, city)
works (Lastname, FirstName, MidInitial, company_name, salary)
manages(Lastname, FirstName, MidInitial, ManagerLastname, MFirstName, MMidInitial, start-date)

It's not the most elegant piece of code, but try using this one:
WITH tab AS (
SELECT DISTINCT W1.COMPANY_NAME,
E1.CITY
FROM EMPLOYEE E1
JOIN WORKS W1 ON (E1.firstname = W1.firstname AND E1.lastname = W1.lastname)
WHERE E1.CITY IN (SELECT DISTINCT E2.CITY
FROM EMPLOYEE E2,WORKS W2
WHERE E2.firstname = W2.firstname
AND E2.lastname = W2.lastname
AND W2.COMPANY_NAME= 'Mutual of Omaha')
)
SELECT tab.COMPANY_NAME
FROM tab
GROUP BY tab.COMPANY_NAME
HAVING COUNT(tab.CITY) = (SELECT COUNT(sub.CITY) FROM tab sub WHERE COMPANY_NAME = 'Mutual of Omaha')

This option uses the bitwise operator so is also limited by the number of cities it can use at once (~31 due to Int size limit)
------------Assign each distinct city a value that is x2 the previous (like binary counting)
create table #cityvalues (CityName varchar(100), ValueField int)
select distinct E1.CITY
into #while
FROM EMPLOYEE E1
while (select count(*) from #while) > 0
begin
insert into #cityvalues
select top 1 CITY, coalesce((select max(ValueField) from #cityvalues)*2, 1) from #while
delete from #while w where w.CITY in (select CityName from #CityValues)
end
--------------------------------------------------------------------------------
--------------Create a list of Company/City--------------------------------
create table #companycities (Compname varchar(100),CityName varchar(100))
insert into #companycities
select distinct
W1.COMPANY_NAME
,E1.CITY
FROM EMPLOYEE E1
JOIN WORKS W1 on E1.firstname = W1.firstname AND E1.lastname = W1.lastname
----------------------------------------------------------------------------
----This SUM function then creates a "list" of all cities for the company in a single field
select cc.Compname, sum(cv.ValueField) as AllCities
into #CompanyAllCities
from #companycities cc
join #cityvalues cv on cc.CityName = cv.CityName
group by Compname
-----------------------------------------------------------------
----------This query checks if the company's "list" contains the "list" for the joined company, excluding itself
select distinct cac1.Compname
from #CompanyAllCities cac1
join #CompanyAllCities cac2 on cac2.Compname = 'Mutual'
where cac2.AllCities & cac1.AllCities = cac2.AllCities
and cac1.Compname <> 'Mutual'
-------------------------------------------------------------
----------Tidy up after yourself----------
drop table #cityvalues,#CompanyAllCities,#companycities
-----------------------------------------

I would start with a CTE that has each company with the cities they operate in.
Then there are several options, but a self-join with aggregation does the count that you want:
with cw as (
select distinct e.city, w.company_name
from employee e join
works w
on e.firstname = w.firstname and
e.lastname = w.lastname and
e.midinitial = w.midinitial
)
select cw.company_name
from cw join
cw cwo
on cw.city = cwo.city and
cwo.company_name = 'Mutual of Omaha'
group by cw.company_name
having count(*) = (select count(*) from cw where
cw.company_name = 'Mutual of Omaha');

You can list and weight the cities where companies have employees, so you will get a single number representing each company based on where they have their employees, a sort of group number.
Then you can check which cities have the same group number.
;with
cities as(select ROW_NUMBER() over (order by city) city_id, city from (select distinct city from employees) c),
chk as (
select distinct company_name, city_id
from works w
join employees e on w.firstname = e.firstname and w.lastname = e.lastname
join cities c on c.city = e.city
),
cnt as (
select company_name, SUM(power(cast(2 as bigint), city_id-1)) n
from chk
group by company_name
)
select company_name
from cnt
where n = (select n from cnt where company_name = 'Mutual of Omaha')

Related

List the surnames of bosses who manage at least two employees from the query below

I need to get the surnames of bosses who manage at least two employees from the query below that earn no more than twice the average earnings of ALL people they direct.
I'm stuck here:
SELECT surname from emp k INNER JOIN
(SELECT surname, base_salary
from emp p LEFT JOIN
(select id_team, avg(base_salary) as s, count(*) as c from emp group by id_team)
as o ON(p.id_team = o.id_team)
where p.base_salary between o.s*0.7 and o.s*1.3 and o.c >=2) l ON (k.id_boss = o.id_boss)
having count(k.id_boss) >2 ??? AND k.base_salary < ????
I hope you get my point. Any advices how could I do that?
Here's what the full table looks like:
Based on your query you should just add the proper having clause
having count(k.id_boss) >2 AND k.base_salary < 2*l.s
SELECT k.surname
from emp k
INNER JOIN (
SELECT surname, base_salary, id_boss
from emp p
LEFT JOIN (
select id_team, avg(base_salary) as s, count(*) as c
from emp
group by id_team
) o ON p.id_team = o.id_team
where p.base_salary between o.s*0.7 and o.s*1.3 and o.c >=2
) l ON k.id_boss = o.id_boss
group by k.surname
having count(l.id_boss) >2
AND k.base_salary < 2*l.s

Missing expression problem in SQL using Oracle

I want to get the number of employees by department and I wrote this script using Oracle but it always says that there is a missing expression
The columns used in my tables :
department :name (the name of the department) -
depnum (the id of the department"primary key"),
employee : empnum (the id of the employee) -
depnum (the id of the department in which the employee in question is working "foreign key")
Query:
select
s.name
from
department s
inner join
employee p on s.depnum = p.depnum
group by
s.name
having
count(p.empnum) = max(select count(p.empnum)
from employee p, department s
where s.depnum = p.depnum
group by s.name) ;
If you want the number of employees by department, I would expect something like this:
select s.name, count(*) as num_employees
from department s inner join
employe p
on s.depnum = p.depnum
group by s.name ;
If you want the department names with the maximum number of names, you can use a having clause:
select s.name, count(*) as num_employees
from department s inner join
employe p
on s.depnum = p.depnum
group by s.name
having count(*) = (select max(cnt)
from (select count(*) as cnt
from employee e2
group by e2.depnum
) e2
);
The problem with your query is that you are attempting to take the max() of a subquery. That syntax is not allowed -- and not necessary.
you sql statement is not correct that's why it thrown that error. I think you tried something like below
select s.name
from department s
inner join employe p on s.depnum=p.depnum
group by s.name
having count(p.empnum)=
select max(cnt) from
(
select count(p.empnum) as cnt
from employe p join department s
on s.depnum=p.depnum
group by s.name
) t;

Using SQL Group By while keeping same varchar values

I have a query that is returning two values. I want to have the largest value so I do a group by, then MAX. However, I have three other columns(varchar) that I would like to remain consistent with the id that is brought in with max.
Example.
OId CId FName LName BName
18477 110 Hubba Bubba whoa
158 110 Test2 Person2 leee
What I want is
OId CId FName LName BName
18477 110 Hubba Bubba whoa
So I want to group them by CId. And O Id I want to keep the largest number. I can't use Min or Max for the FName, LName, or BName because I want them to be the one with the OId that is selected. The FName, LName and BName for the other row I don't even want/need.
I tried using SELECT TOP, but that only pulls in literally one row and I need multiple.
SQL
INSERT INTO #CustomerInfoAll(FName, LName, BName, OwnerId, CustomerId)
SELECT
-- what goes here --(o.FirstName) AS FName,
-- what goes here --(o.LastName) AS LName,
-- what goes here --(o.BusinessName) AS BName,
MAX(o.OId) AS OId,
(r.CId) AS CId
FROM Owner o
INNER JOIN Report r
ON o.ReportId = r.ReportId
WHERE r.CId IN (SELECT CId FROM #ThisReportAll)
AND r.Completed IS NOT NULL
GROUP BY r.CId
ORDER BY OId DESC;
Assuming you have SQL Server 2005 or higher:
INSERT INTO #CustomerInfoAll (FName, LName, BName, OwnerId, CustomerId)
SELECT
FirstName,
LastName,
BusinessName,
Id,
CId
FROM
(
SELECT
Seq = ROW_NUMBER() OVER (PARTITION BY r.CId ORDER BY o.Id DESC),
o.Id,
r.CId,
o.FirstName,
o.LastName,
o.BusinessName
FROM
dbo.Owner o
INNER JOIN dbo.Report r
ON o.ReportId = r.ReportId
WHERE
EXISTS ( -- can be INNER JOIN instead if `CId` is unique in temp table
SELECT *
FROM #ThisReportAll tra
WHERE r.CId = tra.CId
)
AND r.Completed IS NOT NULL
GROUP BY
o.Id,
r.CId,
o.FirstName,
o.LastName,
o.BusinessName
) x
WHERE
x.Seq = 1;
DO use full schema names on all your objects (dbo.Owner and dbo.Report).
DO use a semi-join (an EXISTS clause) or INNER JOIN instead of IN when possible.

SQL query to find students who received an A for every course taken

I have following schema:
Students(sid, firstname, lastname, status, gpa, email)
Courses(dept_code, course#, title)
Classes(classid, dept_code, course#, sect#, year, semester, limit, class_size)
Enrollments(sid, classid, lgrade)
I need some help to find out all the students who received an A for every course taken.
I might suggest doing this with an aggregation:
select e.sid
from enrollement e
group by e.sid
having min(lgrade) = max(lgrade) and min(lgrade) = 'A';
try this
select * from students
where sid not in (select distinct sid from enrollement where coalesce (lgrade,'X') <> 'A')
It means: take all students where none of his/her grade is other than A
if you wanna to get also the name of the class and course, you have to join also both tables
Try not to overthink this:
SELECT s.*
FROM STUDENTS s
WHERE s.GPA = 4.0
That'll work for the A=4, B=3, C=2, D=1, F=0 case (standard American grading system).
For non-standard systems (such as my kids high school where A=5 for honors and advanced placement classes) we can't trust the GPA:
SELECT s.*
FROM STUDENTS s
INNER JOIN (SELECT SID, COUNT(*) AS CLASS_COUNT
FROM ENROLLMENTS
GROUP BY SID) cc
INNER JOIN (SELECT SID, COUNT(*) AS A_GRADE_COUNT
FROM ENROLLMENTS
WHERE LGRADE = 'A'
GROUP BY SID) ag
ON ag.SID = s.SID
WHERE CLASS_COUNT = A_GRADE_COUNT
Best of luck.
I think this is the clearest:
select e.sid
from enrollement e
group by e.sid
having count(case when lgrade = 'A' then 1 else 0 end) = count(*) and count(*) > 0

SELECT clause with MINUS INTERSECT and UNION

In my database, i created a table name DEPTLOC
//DEPTLOC
DNAME CITY
----------------------------
RESEARCH BOSTON
IT PARIS
SCIENCE LONDON
RESEARCH LONDON
SALES NEWYORK
RESEARCH PARIS
RESEARCH NEWYORK
MARKETING NEWYORK
So i used the following query
SELECT CITY FROM DEPTLOC
INTERSECT
(
SELECT CITY FROM DEPTLOC WHERE DNAME='SALES'
UNION
SELECT CITY FROM DEPTLOC WHERE DNAME='RESEARCH'
);
But my output is all the CITY will be displayed. My question is want find which of the DNAME='SALES' OR DNAME='RESEARCH' has its location in all cities.
So from the table above, all distinct city are
CITY
-------
BOSTON
PARIS
LONDON
NEWYORK
Since 'RESEARCH' have all the location but 'SALES' Only have some, my output should be display like this
DNAME
---------
RESEARCH
What should i change for my query in order to get the correct output
Here's a shorter (than my previous answer) query that checks only between DNAMES SALES and RESEARCH to see which DNAME has all CITIES. Set operators UNION ALL and MINUS are used.
SELECT DNAME
FROM
(
SELECT 'SALES' DNAME, COUNT(*) MISSING_CITIES
FROM
(
SELECT DISTINCT CITY FROM DEPTLOC
MINUS
SELECT CITY FROM DEPTLOC WHERE DNAME = 'SALES'
)
UNION ALL
SELECT 'RESEARCH', COUNT(*) FROM
(
SELECT DISTINCT CITY FROM DEPTLOC
MINUS
SELECT CITY FROM DEPTLOC WHERE DNAME = 'RESEARCH'
)
)
WHERE MISSING_CITIES = 0;
One way of doing this is to count the distinct locations, and join it on the departments query:
SELECT dname
FROM (SELECT dname, COUNT(*) AS dept_city_count
FROM deptloc
GROUP BY dname) d
JOIN (SELECT COUNT (DISTINCT city) AS city_count
FROM deptloc) ON city_count = dept_cirt_count
Here is a query using joins. First all Cities are selected. Then, a CROSS JOIN is made between all Departments and all Cities. Then, a LEFT JOIN is made to select Departments that do not have all Cities. Then, another LEFT JOIN is made to select Departments that do have all Cities.
SELECT DISTINCT
DL.DNAME
FROM
DEPTLOC DL
LEFT JOIN
(
SELECT
ALL_COMBOS.DNAME dname
FROM
(
SELECT DISTINCT
D1.DNAME DNAME,
D2.CITY CITY
FROM
DEPTLOC D1
CROSS JOIN
(
SELECT DISTINCT
CITY
FROM
DEPTLOC
)
D2 --All distinct Cities
)
ALL_COMBOS --All Departments with all Locations
LEFT JOIN DEPTLOC D2
ON
ALL_COMBOS.DNAME = D2.DNAME
AND ALL_COMBOS.CITY = D2.CITY
WHERE
D2.DNAME IS NULL
)
NOT_ALL_LOCATIONS --Departments that do not have all Locations
ON DL.DNAME = NOT_ALL_LOCATIONS.DNAME
WHERE
NOT_ALL_LOCATIONS.DNAME IS NULL; --Departments that have all Locations
As it can be observed, this method does not rely on the count of the Cities, but on the actual values of the Cities themselves.
Assuming that the key in deptloc is (dname, city) then the following would work:
select dname
from deptloc
group by dname
having count(*) = (select count(distinct city) from deptloc);
The query works by creating one group for every department. The count(*) would be the nr of cities that the department is located in. Each such count is compared with the nr of unique Cities cited in the same table.
Edited: Saw your comment now, that this was the solution you did not want :)
Here is another version of "relational division"
select departments.dname
from (select distinct dname from deptloc) departments
where not exists(
select 'x'
from (select distinct city from deptloc) cities
where not exists(
select 'x'
from deptloc x
where x.city = cities.city
and x.dname = departments.dname));