Figure out which employee has done more training - sql

I have 2 tables, employees and classes_taken. I am trying to see which employees has taken more than 2 class. However, I am getting this error ERROR: aggregate functions are not allowed in WHERE Position: 87.
CREATE TABLE employees (
id integer primary key,
name text
);
CREATE TABLE classes_taken (
employee integer,
class text,
foreign key (employee) references employees(id)
);
INSERT INTO employees (id, name) VALUES
(1, 'bob'), (2, 'sam'), (3, 'mike');
INSERT INTO classes_taken (employee, class) VALUES
(1, 'swimming'), (1, 'dancing'), (2, 'swimming'), (2, 'tennis'), (3, 'golf'), (3, 'dancing');
My select statement.
select e.id, e.name
FROM employees e
JOIN classes_taken c
ON e.id = c.employee
WHERE count(c.class) > 2
GROUP BY c.class;
SQLFiddle: http://sqlfiddle.com/#!15/5296cb/4

You need to keep the count filter in a HAVING clause, because it is on an aggregated value.
select e.id, e.name
FROM employees e
INNER JOIN classes_taken c ON e.id = c.employee
GROUP BY e.id, e.name
HAVING count(c.class) > 2

Related

How many elements in one column are linked to an element other column?

Consider I have two tables
Courses Program
---------------------------
course_ID program_id
course_title program_name
program_ID
Now, I want to check no of courses(by course_id) offered by each program (program_id).
select c.program_id ,p.program_name, count(course_id)
from courses c
join Program p on c.Program_id =p.Program_id
group by program_id,program_name
If I understood you correctly, you're searching for a GROUP BY and a corresponding aggregate.
--Creating sample tables and data
SELECT course_ID, course_title, program_ID
INTO #courses
FROM (
VALUES (0, 'course_0', 0),
(1, 'course_1', 0),
(2, 'course_2', 0),
(3, 'course_3', 0),
(4, 'course_4', 1),
(5, 'course_5', 1),
(NULL, 'course_6', 1)
) AS C (course_ID, course_title, program_ID)
SELECT program_ID, program_title
INTO #programs
FROM (
VALUES (0, 'program_0'),
(1, 'program_1')
) AS P (program_ID, program_title)
and after that execute the query
SELECT P.program_title, COUNT(C.course_ID) AS courses_amount
FROM #courses C
INNER JOIN #programs P ON C.program_ID = P.program_ID
GROUP BY P.program_ID, P.program_title
So you basically GROUP BY the value to which you to aggregate to and COUNT the 'course_id'.
COUNT(C.course_ID) only counts actual values and will ignore NULLs.
If you want to count the NULLs as well, just use COUNT(*).
EDIT: Forgot the result...
So it'll look like this:
program_title
courses_amount
program_0
4
program_1
2

How to request JSON among three tables

Suppose I have 3 tables employee, child and employee_child.
The first table contains id and name columns, the second contains id and name as well, and in the last contains id, employee_id, child_id.
Those tables (employee and child) are associated in the table employee_child.
To help, I let these queries to populate those tables:
CREATE TABLE employee (
id INTEGER PRIMARY KEY,
name VARCHAR(128))
INSERT INTO employee(id, name)
VALUES(1, 'Josh'), (2, 'Michel'), (3, 'Will')
CREATE TABLE child (
id INTEGER PRIMARY KEY,
name VARCHAR(128))
INSERT INTO child(id, name)
VALUES(1, 'Karen'), (2, 'Adam'), (3, 'Ellen')
CREATE TABLE employee_child (
id INTEGER PRIMARY KEY,
employee_id INTEGER NOT NULL,
child_id INTEGER NOT NULL)
ALTER TABLE employee_child ADD FOREIGN KEY (employee_id) REFERENCES employee (id)
ALTER TABLE employee_child ADD FOREIGN KEY (child_id) REFERENCES child (id)
INSERT INTO employee_child(id, employee_id, child_id)
VALUES(1, 1, 1), (2, 1, 2), (3, 2, 3)
Results of selects from tables
I need to get the employee's name and his child's names by employee.id using JSON request, and get the following answer:
{
"id":1,
"name":"Josh",
"children":[
{
"id":1,
"name":"Karen"
},
{
"id":2,
"name":"Adam"
}
]
}
I've tried another way, but the most right answer that I got was this following query:
SELECT [''] = (SELECT e.id, e.name
FROM employee e
WHERE e.id = 1
FOR JSON PATH),
[children] = (SELECT c.id, c.name
FROM child c
INNER JOIN employee_child ec ON ec.child_id= c.id
WHERE ec.employee_id = 1
FOR JSON PATH)
FOR JSON PATH
I'd like to get a support to achieve the fully answer.
FOR JSON AUTO seems to get you exactly what you want:
SELECT e.id,
e.name,
children.id,
children.name
FROM dbo.employee e
JOIN dbo.employee_child ec ON e.id = ec.employee_id
JOIN dbo.child children ON ec.child_id = children.id
WHERE e.id = 1
FOR JSON AUTO;

How to I find the person who has taught the most classes

I want to try and find the employee who has taught the most classes as the position Teacher. So in this I want to print out Nick, as he has taught the most classes as a Teacher.
However, I am getting the error:
ERROR: column "e.name" must appear in the GROUP BY clause or be used in an aggregate function Position: 24
CREATE TABLE employees (
id integer primary key,
name text
);
CREATE TABLE positions (
id integer primary key,
name text
);
CREATE TABLE teaches (
id integer primary key,
class text,
employee integer,
position integer,
foreign key (employee) references employees(id),
foreign key (position) references positions(id)
);
INSERT INTO employees (id, name) VALUES
(1, 'Clive'), (2, 'Johnny'), (3, 'Sam'), (4, 'Nick');
INSERT INTO positions (id, name) VALUES
(1, 'Assistant'), (2, 'Teacher'), (3, 'CEO'), (4, 'Manager');
INSERT INTO teaches (id, class, employee, position) VALUES
(1, 'Dancing', 1, 1), (2, 'Gardening', 1, 2),
(3, 'Dancing', 1, 2), (4, 'Baking', 4, 2),
(5, 'Gardening', 4, 2), (6, 'Gardening', 4, 2),
(7, 'Baseball', 4, 1), (8, 'Baseball', 2, 1),
(9, 'Baseball', 4, 2);
The SQL statement I am trying to use:
SELECT count(t.class), e.name
FROM positions p
JOIN teaches t
ON p.id = t.position
JOIN employees e
ON e.id = t.employee
WHERE p.name = 'Teacher'
GROUP BY t.employee;
I've been working on this on a sql fiddle:
http://www.sqlfiddle.com/#!17/a8e19c/3
Your query looks pretty good. You just need to fix the GROUP BY clause, so it is consistent with the columns in the SELECT clause. Then ORDER BY and LIMIT:
SELECT count(*) cnt_classes, e.name
FROM positions p
INNER JOIN teaches t ON p.id = t.position
INNER JOIN employees e ON e.id = t.employee
WHERE p.name = 'Teacher'
GROUP BY e.id --> primary key of "employees"
ORDER BY cnt_classes DESC --> order by descending count of classes
LIMIT 1 --> keep the first row only
In your select you are using aggregate COUNT that counts all lines in each group (GROUP BY t.employee) but you don't aggregate e.name.
So for Nick you basically select 4 rows each for one class that have two columns - class name and teacher name. Then you ask server to count class names in Nicks group (by his employee id), that aggregates 4 rows into one with value 4 but you don't do anything about teacher name so you are left with invalid structure where you have 1 row for classes count column and 4 rows for teacher name. Same for other teachers. And that's what server is complaining about. Easiest way to fix that is to add e.name to GROUP BY, that will squeeze those 4 rows of same value into one.
To get teacher that teaches most classes you then only need to sort results by class count descending order and limit result count to 1. That will give you result row with highest class count.
Updated fiddle: http://www.sqlfiddle.com/#!17/a8e19c/7
You're getting the error because you need to need to have every column you're selecting (e.name in this example in the GROUP BY clause, otherwise SQL doesn't know how to group and return a count for that column. You'll also want to use TOP(1) and order by if you want to return the person with the most.
SELECT TOP(1) count(*), e.name
FROM teaches t
INNER JOIN positions p ON t.position = p.id
INNER JOIN employees e ON e.id = t.employee
WHERE p.name = 'Teacher'
GROUP BY e.name
ORDER BY count(*) DESC;

Aggregation Sum in SQL (Joins)

I'm having some trouble with summing some column in my query with aggregation.
It's a bit difficult to describe what is happening but I'll try my best:
I have 3 tables - details, extra details and places.
Places is a table that contains places in the world. Details contains details about events that happened, and extra details provides some more data on the events. Each place has an ID and a ParentID (Like New York has an ID and it's parent ID is the US. Something like that). The ID of the event(details) appears a number of times as a column in the extra details table. The extra details table also holds the ID of the place that that event occurred at.
OK after all of that, what I'm trying to achieve is, for each place, the sum of the events that happened there. I know it sounds very specific, but it's what the client asked. Anyhow, example of what I'm trying to get to:
NewYork 60, Chicago 20, Houston 10 Then the US will have 90. And it has several levels.
So this is what I was trying to do:
With C(ID, NAME, COUNTT, ROOT_ID) as
(
SELECT d.ID, d.NAME,
(SELECT COUNT(LX.ID) as COUNTT
FROM EXTRA LX
RIGHT JOIN d ON LX.PLACE_ID = d.ID -- ****
GROUP BY d.ID, d.NAME),
d.ID as ROOT_ID
FROM PLACES d
UNION ALL
SELECT d.ID, d.NAME,
(SELECT COUNT(LX.ID) as COUNTT
FROM EXTRA LX
RIGHT JOIN d ON LX.PLACE_ID = d.ID
GROUP BY d.ID, d.NAME),
C.ROOT_ID
FROM PLACES dx
INNER JOIN C ON dx.PARENT_ID = C.ID
)
SELECT p.ID, p.NAME, S.SumIncludingChildren
FROM places p
INNER JOIN (
SELECT ROOT_ID, SUM(COUNTT) as SumIncludingChildren
FROM C
GROUP BY ROOT_ID
) S
ON p.ID = S.ROOT_ID
ORDER BY p.ID;
The details table is only for showing their data. I'll add that later. It's only comparing the respective columns. To making it work I don't need that. Only for the site data.
It doesn't work because it doesn't recognizes the 'd' where the '****' is. If I'll put a 'new instance' of that table, it won't work either. So I tried to replicate what the right join by doing 'NOT EXISTS IN' on a query that gets all the places instead of the right join...on. Same problem.
Maybe I don't get something. But I'm really seeking a solution and some explanation. I know my code isn't perfect.
Thanks in advance.
EDIT: I'm using OracleSQL on Toad 10.6
create table p(id number, up number, name varchar2(100));
create table e(id number, pid number, dsc varchar2(100));
insert into p values (1, null, 'country');
insert into p values (2, 1, 'center');
insert into p values (3, 1, 'province');
insert into p values (4, 2, 'capital');
insert into p values (5, 2, 'suburb');
insert into p values (6, 3, 'forest');
insert into p values (7, 3, 'village');
insert into p values (8, 7, 'shed');
insert into p values (9, 2, 'gov');
insert into e values (1, 8, 'moo');
insert into e values (2, 8, 'clank');
insert into e values (3, 7, 'sowing');
insert into e values (4, 6, 'shot');
insert into e values (5, 6, 'felling');
insert into e values (6, 5, 'train');
insert into e values (7, 5, 'cottage');
insert into e values (8, 5, 'rest');
insert into e values (9, 4, 'president');
insert into e values (10,1, 'GNP');
commit;
with
places as
(select id,
up,
connect_by_root id as root,
level lvl
from p
connect by prior id = up),
ev_stats as
(select root as place, max(lvl) as max_lvl, count(e.id) as ev_count
from places left outer join e
on places.id = e.pid
group by root)
select max_lvl, p.name, ev_count
from ev_stats inner join p on p.id = ev_stats.place
order by max_lvl desc;

How to solve this unique SQL query?

I have the following tables:
Student(Sid,Sname) Primary key: {sid}
Course(cid,cname,duration,fee) Primary key:{cid}
Enrolled(sid,cid) Foreighn key: {sid,cid}
Query: Find the maximum fees paid by each student where a student can
enroll in different courses.
My attempt:
SELECT ssid, max(fee) as MAX_FEES from (Select sid as ssid, C.cid asccid,
fee from Course C,Enrolled E where C.cid = E.cid) group by
rollup(ssid,ccid,fee)
However, this doesn't gives the desired output appropriately. How to output only the Highest fees paid by each student?
try
SELECT max(c.fee) from course c, student s, enrolled e where s.sid=e.sid and e.cid=c.cid group by e.sid;
You didn't say if you also needed to list the students who are not enrolled in any course, so I'll provide one more solution:
CREATE TABLE student (
sid NUMBER PRIMARY KEY,
sname VARCHAR2(40)
);
CREATE TABLE course (
cid NUMBER PRIMARY KEY,
cname VARCHAR2(40),
duration NUMBER,
fee NUMBER
);
CREATE TABLE enrolled (
sid NUMBER,
cid NUMBER,
PRIMARY KEY (sid, cid),
FOREIGN KEY (sid) REFERENCES student (sid),
FOREIGN KEY (cid) REFERENCES course (cid)
);
INSERT INTO student (sid, sname) VALUES (1, 'John');
INSERT INTO student (sid, sname) VALUES (2, 'Peter');
INSERT INTO student (sid, sname) VALUES (3, 'Jake');
INSERT INTO course (cid, cname, duration, fee) VALUES (1, 'Math', 1, 1000);
INSERT INTO course (cid, cname, duration, fee) VALUES (2, 'Physics', 1, 1500);
INSERT INTO enrolled (sid, cid) VALUES (1, 1); -- John taking Math
INSERT INTO enrolled (sid, cid) VALUES (1, 2); -- John taking Physics
-- Peter being lazy
INSERT INTO enrolled (sid, cid) VALUES (3, 1); -- Jake taking Math
COMMIT;
-- not taking lazy (not taking any courses) students under account
SELECT s.sid, MAX(c.fee)
FROM student s
JOIN enrolled e ON (e.sid = s.sid)
JOIN course c ON (e.cid = c.cid)
GROUP BY s.sid
;
-- all students
SELECT s.sid, NVL(MAX(c.fee), 0)
FROM student s
LEFT JOIN enrolled e ON (e.sid = s.sid)
LEFT JOIN course c ON (e.cid = c.cid)
GROUP BY s.sid
;