Reputation: 445
I have a database that contains 3 tables.
Employee(SSN, Fname, Lname, Salary)
Project(Pnumber, Pname, Plocation)
Works_on(Essn, Pno, Hours)
I want to create a view that for each project shows its name (Pname), how many employees who work on that project getting paid more than 38k (More) and how many employees who work on that project getting paid less than 38k (Less).
My appraoch
create view compare as
(
(select count(E.SSN) as More, P.Pname
from Employee as E, Project as P, Works_on as W
where E.Salary > 38000 and W.Pno = P.Pnumber and E.SSN = W.Essn
group by(P.Pname)) union
(select count(E.SSN) as Less, P.Pname
from Employee as E, Project as P, Works_on as W
where E.Salary < 38000 and W.Pno = P.Pnumber and E.SSN = W.Essn
group by(P.Pname))
);
But it shows only the (More) col and the project name.
Upvotes: 2
Views: 82
Reputation: 3225
create view compare as
select sum(case when Salary >= 38000 then 1 else 0 end) as More,
sum(case when Salary < 38000 then 1 else 0 end) as Less,
sum(case when Salary >= 38000 then 1 else -1 end) as Diff,
Pname
from Project join Works_on on Pno = Pnumber
join Employee on SSN = Essn
group by Pname;
Upvotes: 0
Reputation: 279
If you are sure about going for a view approach, this might work for you
select p.Pname
, sum(case when e.Salary < 38000 then 1 else 0 end) as under_38_k
, sum(case when e.Salary > 38000 then 1 else 0 end) as over_38_k
from Works_on w
join Employee e on e.ssn = w.essn
join Project p on p.Pnumber = w.Pno
group by p.Pname
order by p.Pname asc --customize as needed
Considerations:
Since we are using JOIN, which is short for INNER JOIN, projects with no assigned employees will not be displayed within the results. Switch to LEFT OUTER JOIN to Projects table if you'd like those to be showed as well with 0 as values.
Employees making exactly 38k are outside of the results as well. Feel free to include them by changing the case statement for one of the two columns.
If you want to make that limit customizable without redifining the view, you can define a function which takes the limit as a parameter like this:
CREATE FUNCTION EmployeesEarnings(@limit int)
RETURNS TABLE
AS
RETURN
select p.Pname
, sum(case when e.Salary < @limit then 1 else 0 end) as under_limit
, sum(case when e.Salary > @limit then 1 else 0 end) as over_limit
, sum(case when e.Salary < @limit then 1 else 0 end) - sum(case when e.Salary > @limit then 1 else 0 end) as difference
from Works_on w
join Employee e on e.ssn = w.essn
join Project p on p.Pnumber = w.Pno
group by p.Pname
and then call the function as if it was a table:
select *
from EmployeesEarnings(38000)
Upvotes: 3
Reputation: 726579
Use conditional SUM
in place of COUNT
:
select
SUM(CASE WHEN E.Salary >= 38000 THEN 1 ELSE 0 END) as More
, SUM(CASE WHEN E.Salary < 38000 THEN 1 ELSE 0 END) as Less
, P.Pname
from Employee as E
join Works_on as W ON E.SSN = W.Essn
join Project as P ON W.Pno = P.Pnumber
group by(P.Pname)
Note the use of ANSI joins (you should switch to ANSI syntax) and use of >=
in place of >
(otherwise Salary
of exactly 38000 would not get counted).
Upvotes: 2
Reputation: 95561
You've misunderstanding how UNION
works. UNION
combines the dataset, it doesn't add exact columns to it; and it uses the alias's supplied in the first query. Thus your column, Less
, will have it's values in the output of column More
. If you want the columns columns to be displayed, try adding a column with a value of NULL
to both datasets. Also, get rid of those awful implicite JOINs. Use JOIN
and ON
:
CREATE VIEW Compare AS
SELECT COUNT(E.SSN) as More,
NULL AS Less,
P.Pname
FROM Employee E
JOIN Project P ON W.Pno = P.Pnumber
JOIN Works_on W ON E.SSN = W.Essn
WHERE E.Salary > 38000
GROUP BY P.Pname
UNION
SELECT NULL as More,
COUNT(E.SSN) AS Less,
P.Pname
FROM Employee E
JOIN Project P ON W.Pno = P.Pnumber
JOIN Works_on W ON E.SSN = W.Essn
WHERE E.Salary < 38000
GROUP BY P.Pname;
This, however, can be simplified to
CREATE VIEW Compare AS
SELECT CASE WHEN E.Salary > 38000 THEN COUNT(E.SSN) END AS More,
CASE WHEN E.Salary < 38000 THEN COUNT(E.SSN) END AS Less,
P.Pname
FROM Employee E
JOIN Project P ON W.Pno = P.Pnumber
JOIN Works_on W ON E.SSN = W.Essn
GROUP BY P.Pname;
Upvotes: 1
Reputation: 20804
First, a view might not be appropriate for this situation because the number 38000 might change over time.
Second, a case construct seems appropriate for this situation. The general idea is:
select case when salary >= 38000 then 'more' else 'less' end salaryRange
, count(*) employees
etc
group by case when salary >= 38000 then 'more' else 'less' end
Upvotes: 0