SQL practice troubles for beginner TYT - sql

i've just study SQL for a month and there is many things I'm still cannot get a hold of. Can anyone help me, plz provide the results with some explaination, I need to understand it for future uses. I only list things that I cannot understand.
List the ID of the managers and the total number of employees reporting to each of them. Display the result in descending order of the total number of employees
I can do:
SELECT employeeNumber
FROM employees
WHERE jobTitle LIKE '%Manager%'
UNION
SELECT employeeNumber, COUNT(*)
FROM employees
WHERE reportsTo 'WHICH CONDITION?'
ORDER BY COUNT(*) DESC
Can someone fill in after 'reportTo', I cant find a condition that's working TYT
Display all offices and their counts in each country
I think this mean showing a table with every country and count total number of offices in that country
I can do:
SELECT country, COUNT(*)
FROM offices
GROUP BY country
UNION
SELECT country, officeCode
FROM offices
But the results is not as expected

select
reportsTo,
COUNT(employeeNumber) as numberOfEmployees
from employees
group by reportsTo
Will give you a count of employeeNumbers that report to that reportsTo.
This will not give you the managers where nobody is reporting to, so to do that you would have to make a JOIN:
SELECT
a.employeeNumber AS managerNumber,
COUNT(b.employeeNumber) AS numberOfEmployees
FROM employees AS a
LEFT JOIN employees AS b on (b.reportsTo=a.employeeNumber)
WHERE a.jobTitle LIKE '%Manager%'
GROUP BY a.employeeNumber

Related

Not Sure How NOT EXISTS works

This is just homework currently, but I am having trouble writing a SELECT statement for this question:
Produce a result set showing department name, department description,
employee first name, and employee last name for ALL departments,
including those for whom no employee has been assigned. Sort by
department name.
I believe I have the SELECT, FROM, WHERE, and ORDER BY down, but the NOT EXISTS is where I am struggling.
Here is the table:
SELECT deptName, deptDesc, empFirstName, empLastName
FROM department, employee
WHERE department.deptID=employee.deptID
AND NOT EXISTS (
SELECT deptName, deptDesc
FROM
ORDER BY deptName ;
At this point I am just trying to include those for whom no employee has been assigned.
I believe you are looking for a LEFT JOIN instead: https://www.w3schools.com/sql/sql_join_left.asp
You want to include everything from department, and also anything that matches from employee, but not just the intersection of the two.
NOT EXISTS will just return a boolean true or false if that inner query produces results with at least one row. I don't think that's what you want.
SELECT deptName, deptDesc, empFirstName, empLastName
FROM department
LEFT JOIN employee on department.deptID=employee.deptID
ORDER BY deptName;

Find Name of person in a city with Maximum age

Here the following Query will give the result for Average Age and number of residents in a City. Group BY applied on City and Aggregated functions on residents as Count and Age as AVG.
select city, count(*) as residents, avg(age) as AverageAge
from people
group by city
Similarly I want the Name of the Resident in a City With Maximum Age
select city, name, max(age) as AverageAge
from people
group by city
But this query is not working as name is not there in the Group By clause and neither used as a Aggregated function
Can you please help me in this.
Not the cleanest way, but you can do:
SELECT P1.*
FROM People P1
JOIN
(
SELECT City, MAX(Age) AS Age
FROM People
GROUP BY City
) P2 ON P1.Age = P2.Age AND P1.City = P2.City
UPDATE: When taking execution plans into account, the most performant query I can figure out seems to be the one below; it also seems to give the result that makes the most sense, if there are multiple people of the same (highest) age, they will all be selected;
SELECT P1.*
FROM people p1
LEFT JOIN people p2
ON p1.city=p2.city AND p1.age < p2.age
WHERE p2.age IS NULL
Basically it'll use a simple left join to get all people that don't have anyone older in the same town.
Otherwise, the simplest to read "general" solution would be using a simple subquery to find the max age of anyone in the town and list people in the city of that age. If several people are of the same (highest) age, it will instead return a random one of them;
SELECT city, name, age
FROM people
WHERE age = (SELECT MAX(age) FROM people p WHERE p.city=people.city)
GROUP BY city;
I added the first query to Aaron's SQLfiddle here, as you can see the first query in this post is the only one of the three without a temporary or filesort.
Try
select city, name, max(age) as AverageAge
from people
group by city, name
sql group by query requires all the column names present in the select
You need a sub query, its a very useful technique sutable for a wide range of solutions. When they are needed they are the only effective solution. I always go to this page http://allenbrowne.com/subquery-01.html when I have forgotten the exact synatax I need (worth bookmarking as its not page 1 on google for sub query which it has been in the past) you want the "TOP n records per group" section. Hope this helps
Definition of "maximum age" : there is no individual (in the same city) with a higher age
SELECT pp.*
FROM people pp
WHERE NOT EXISTS (
SELECT *
FROM people px
WHERE px.city = pp.city
AND px.age > pp.age
)
;
Here is the easiest way to do it in my opinion:
SELECT P.city, P.name, P.age as AverageAge
FROM people P
WHERE P.age = (SELECT MAX(age)
FROM people.P2));

SQL Query using Group by 4

I have tables which looks like below.
Employee table
Date Employee ID Employer ID Salary
2/3/2011 10 20 45666
3/12/2009 43 53 2356
Employer Table
Employer ID State
53 OH
42 MI
Trying to get the total salary by month and by state using group by clause. But not getting the results. what am i doing wrong?? any help appreciated
select date, sum(salary) from employee
group by to_char(date,'MON')
select sum(salary) from employee A, Employer B
where A.employer id=B.employer id
group by B.state
Also i need to get the top 10 distinct employee ids based on their salary
select DISTINCT employee id from employee
where rownum<=10
order by salary desc
You have to group by the exact expression in your select list, e.g.,
select to_char(date,'MON'), sum(salary)
from employee
group by to_char(date,'MON');
You probably want to include the state in your second query:
select b.state, sum(salary)
from employee A, Employer B
where A.employer_id=B.employer_id
group by B.state;
Generally speaking, stating in your question that you're "not getting the results" is not very helpful to the folks you're asking help of. Please provide any error messages or output that describes what "not getting the results" means.

Understanding ROLLUP in SQL

I've figured out CUBE as just generating all the permutations, but I am having trouble with ROLLUP. There don't seem to be any good resources online or in the book I'm reading for explaining SQL for people like me who struggle with it.
My book says that ROLLUP is a special case of the CUBE operator that excludes all cases that don't follow a hierarchy within the results.
I'm not entirely sure what it means, but running it on a table I made kinda produces some useful results.
I made a table from another page on google like this:
Type Store Number
Dog Miami 12
Cat Miami 18
Turtle Tampa 4
Dog Tampa 14
Cat Naples 9
Dog Naples 5
Turtle Naples 1
Then here is query I made:
select store,[type], SUM(number) as Number from pets
group by store, [type]
with rollup
This shows me the number of each type of pet in each store, and total pets in each store, which is kinda cool. If I want to see the query based on pets, I found I have to switch the group by order around so type comes first.
So is rollup based on the first group by clause?
The other question is, I read you use ROLLUP instead of CUBE when you have a year and month column to stop it aggregating the same month across multiple years. I think I understand what this means, but could anyone clarify it? And how do you set it up like this?
Can you use ROLLUP to exclude other combinations of columns as well? My table above is quite simple and the query shows you "pets by store", but if there were other columns, could you include/exclude them from the results?
Best explained through an example. Suppose you group by A, B, C. You then get the following groupings with rollup:
(A, B, C)
(A, B)
(A)
()
So you see that the order is important, as you already found out. If you group by A, C, B, you get the following groupings instead:
(A, C, B)
(A, C)
(A)
()
SELECT departments.department_name
FROM departments
LEFT OUTER JOIN job_history
ON departments.department_id = job_history.department_id
WHERE job_history.employee_id IS NULL;
ex.3
SELECT department_id, manager_id, COUNT() as Numar
FROM Employees
GROUP BY ROLLUP(department_id, manager_id)
UNION
SELECT department_id, manager_id, COUNT() as Numar
FROM Employees
GROUP BY ROLLUP(manager_id, department_id);
SELECT department_id, manager_id, COUNT(employee_id) as employee_count
FROM employee
GROUP BY ROLLUP (department_id, manager_id);
SELECT employee_id, first_name, manager_id
FROM employee
START WITH employee_id = :employee_id
CONNECT BY PRIOR employee_id = manager_id;
ex3.1SELECT department_id, manager_id, COUNT() as Numar
FROM Employees
GROUP BY ROLLUP(department_id, manager_id)
UNION
SELECT department_id, manager_id, COUNT() as Numar
FROM Employees
GROUP BY ROLLUP(manager_id, department_id);

Oracle SQL Query: Getting Largest Sale for Employee

I want to find the largest sale for each of my employees (and display the name of the employee). In MySQL, it's pretty straightforward:
select *
from employee, sale
where employee.id = sale.employee_id
group by employee_id
order by sale.total desc
This does pretty much what one would expect, it would return a list of employees and end up returning the largest sale record with the employee row.
But, Oracle does not allow you to return columns which are not group by expressions when a group by clause is used. Do this make what I do in MySQL "impossible" in Oracle? Or is there some workaround? I suppose I could perform some sort of subquery, but not sure if there is another way to do this that wouldn't quite be so complicated to construct.
Get rid of your select * and replace it with just the columns you need and then group by all the "non-processed" columns.
You'll end up with something like:
select employee.id, employee.name, max(sale.total)
from employee, sale
where employee.id = sale.employee_id
group by employee.id, employee.name
order by max(sale.total) desc
It's a pain - I've had to do this many times before - but just add all the related columns to your group by
To get the largest sale you can use group by with the max function.
select e.name, max (s.total)
from employee e, sale s
where e.id = s.employee_id
group by e.name
order by s.total desc
I have made an assumption that the employee name is in the name column of the employee table. I have also aliased the employee table and sales tables.
If you would prefer to see the total sales for an employee, you can swap out max() and use sum() instead.
Congratulations, you've learned just enough to be dangerous!
What you really want is each employee's largest sale. Now it happens that sorting them by sales amount desc and then grouping them works in MySQL, even though that isn't legal according to ANSI SQL. (Basically, MySQL is arbitrarily grabbing the first row for each employee, and that "works" because of the sort.)
The right way to do this is not to rely on the side of effect of the sort doing what you want; instead you should explicitly ask for what you want: the largest sale for each employee. In SQL that's:
select employee.id, max( sale.total)
from employee, sale
where employee.id = sale.employee_id
group by employee.id
order by 2
If you want to select an employee with a highest sale, you don't need GROUP BY here at all.
All you need is to select the highest sale and join it back to the employees:
SELECT *
FROM (
SELECT sale.*, ROW_NUMBER() OVER (ORDER BY total DESC) AS rn
FROM sale
) s
JOIN employee e
ON e.id = s.employee_id
AND s.rn = 1
This will select a single row with a total highest sale.
If you want to select per-employee highest sale, just add a PARTITION BY clause to your query:
SELECT *
FROM (
SELECT sale.*, ROW_NUMBER() OVER (PARTITION BY employee_id ORDER BY total DESC) AS rn
FROM sale
) s
JOIN employee e
ON e.id = s.employee_id
AND s.rn = 1