select only name by maximal value - sql

I have a request
select
w.Departament_Id, avg(w.Salary) as avgSal,
dep.Name as Name
from
Employee w
join
Departament dep
on
dep.Id = w.Departament_Id
group by
w.Departament_Id,
dep.Name
this request retuns a table contains all avg salary for each departament. Next the target is to select Name of departament with a maximal avgSal value.
How to solve it?

sort by the aggregate and take the top 1:
select TOP 1
w.Departament_Id,
avg(w.Salary) as avgSal,
dep.Name as Name
from Employee w
join Departament dep
on dep.Id = w.Departament_Id
group by
w.Departament_Id,
dep.Name
ORDER BY avg(w.Salary) DESC
The syntax may be slightly different depending on your server software (some will allow ORDER BY avgSal, some will not; some will use LIMIT instead of TOP), but that's the general idea.

Related

For each employee with dependent(s), list their last name and the name of their oldest dependent. Display the results in sequence by dependent’s name

SELECT E.Lname,D.Dependent_name
FROM EMPLOYEE as E , DEPENDENT as D
WHERE
E.ssn=D.Essn
AND
D.Bdate = (SELECT MIN(Bdate) FROM DEPENDENT
WHERE Bdate IS NOT NULL)
This does work but it just show for 1 employee
Link to DB:
https://www.db-fiddle.com/f/xhEj2sAgdTMABBkCtJvmoC/0#&togetherjs=z3CKywAccH
As it is, your subquery returns the minimum bdate over the whole table, resulting in only the empolyee that has the oldest dependant being filtered in. As commented by #PM77-1, you would need to correlate your subquery with the employee that you are currently processing.
SELECT E.Lname,D.Dependent_name
FROM
EMPLOYEE as E
INNER JOIN DEPENDENT as D ON E.ssn=D.Essn
WHERE D.Bdate = (
SELECT MIN(Bdate)
FROM DEPENDENT
WHERE Essn = E.ssn
)
ORDER BY 2, 1;
Another, probably more efficient option, is to use Postgres window function ROW_NUMBER() to assign a row number to each record in the group made of all dependents of a given employee, ordered by date of birth. An outer query can then filter in the first record in each group :
SELECT x.lname, x.dependent_name
FROM (
SELECT
e.lname,
d.dependent_name,
ROW_NUMBER() OVER(PARTITION BY e.ssn ORDER BY d.bdate) rn
FROM employee e
INNER JOIN dependent d ON e.ssn = d.essn
) x WHERE x.rn = 1
ORDER BY 2, 1;
Demo on DB Fiddle
select a.lname, a.dependent_name
from
(
select e.lname, d.dependent_name, d.bdate, e.ssn
from employee e, dependent d
where e.ssn=d.essn
order by d.dependent_name, e.lname
)a
where a.bdate= (select min(bdate) from dependent
where dependent.essn= a.ssn)
order by a.lname;
This displays the names of three employees > Smith, Wallace, Wong with their corresponding eldest dependents > Elizabeth, Abner, Joy.
Hope this solves the problem.

SQL Query - Unsure How to Fix Logical Error

Edit: Sorry! I am using Microsoft SQL Server.
For clarification, you can have a department named "x" with a list of jobs, a department named "y" with a different list of jobs, etc.
I also need to use >= ALL instead of TOP 1 or MAX because I need it to return more than one value if necessary (if job1 has 20 employees, job2 has 20 employees and they are both the biggest values, they should both return).
In my query I'm trying to find the most common jobTitle and the number of employees that work under this jobTitle, which is under the department 'Research and Development'. The query I've written consists of joins to be able to return the necessary data.
The problem I am having is with the WHERE statement. The HAVING COUNT(JobTitle) >= ALL is finding the biggest number of employees that work under a job, however the problem is that my WHERE statement is saying the Department must be 'Research and Development', but the job with the most amount of employees comes from a different department, and thus the output produces only the column names and nothing else.
I want to redo the query so that it returns the job with the largest amount of employees that comes from the Research and Development department.
I know this is probably pretty simple, I'm a noob :3 Thanks a lot for the help!
SELECT JobTitle, COUNT(JobTitle) AS JobTitleCount, Department
FROM HumanResources.Employee AS EMP JOIN
HumanResources.EmployeeDepartmentHistory AS HIST
ON EMP.BusinessEntityID = HIST.BusinessEntityID JOIN
HumanResources.Department AS DEPT
ON HIST.DepartmentID = DEPT.DepartmentID
WHERE Department = 'Research and Development'
GROUP BY JobTitle, Department
HAVING COUNT(JobTitle) >= ALL (
SELECT COUNT(JobTitle) FROM HumanResources.Employee
GROUP BY JobTitle
)
If you only want one row, then a typical method is:
SELECT JobTitle, COUNT(*) AS JobTitleCount
FROM HumanResources.Employee AS EMP JOIN
HumanResources.EmployeeDepartmentHistory AS HIST
ON EMP.BusinessEntityID = HIST.BusinessEntityID JOIN
HumanResources.Department AS DEPT
ON HIST.DepartmentID = DEPT.DepartmentID
WHERE Department = 'Research and Development'
GROUP BY JobTitle
ORDER BY COUNT(*) DESC
FETCH FIRST 1 ROW ONLY;
Although FETCH FIRST 1 ROW ONLY is the ANSI standard, some databases spell it LIMIT or even SELECT TOP (1).
Note that I removed DEPARTMENT both from the SELECT and the GROUP BY. It seems redundant.
And, if I had to guess, your query is going to overstate results because of the history table. If this is the case, ask another question, with sample data and desired results.
EDIT:
In SQL Server, I would recommend using window functions. To get the one top job title:
SELECT JobTitle, JobTitleCount
FROM (SELECT JobTitle, COUNT(*) AS JobTitleCount,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM HumanResources.Employee AS EMP JOIN
HumanResources.EmployeeDepartmentHistory AS HIST
ON EMP.BusinessEntityID = HIST.BusinessEntityID JOIN
HumanResources.Department AS DEPT
ON HIST.DepartmentID = DEPT.DepartmentID
WHERE Department = 'Research and Development'
GROUP BY JobTitle
) j
WHERE seqnum = 1;
To get all such titles, when there are duplicates, use RANK() or DENSE_RANK() instead of ROW_NUMBER().
with employee_counts as (
select
hist.DepartmentID, emp.JobTitle, count(*) as cnt,
case when dept.Department = 'Research and Development' then 1 else 0 end as is_rd,
from HumanResources.Employee as emp
inner join HumanResources.EmployeeDepartmentHistory as hist
on hist.BusinessEntityID = emp.BusinessEntityID
inner join HumanResources.Department as dept
on dept.DepartmentID = hist.DepartmentID
group by
hist.DepartmentID, emp.JobTitle
)
select * from employee_counts
where is_rd = 1 and cnt = (
select max(cnt) from employee_counts
/* where is_rd = 1 */ -- ??
);

Limit the data set of a single table within a multi-table sql select statement

I'm working in an Oracle environment.
In a 1:M table relationship I want to write a query that will bring me each row from the "1" table and only 1 matching row from the "many" table.
To give a made up example... ( * = Primary Key/Foreign Key )
EMPLOYEE
*emp_id
name
department
PHONE_NUMBER
*emp_id
num
There are many phone numbers for one employee.
Let's say I wanted to return all employees and only one of their phone numbers. (Please forgive the far-fetched example. I'm trying to simulate a workplace scenario)
I tried to run:
SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id
WHERE phone.ROWNUM <= 1;
It turns out (and it makes sense to me now) that ROWNUM only exists within the context of the results returned from the entire query. There is not a "ROWNUM" for each table's data set.
I also tried:
SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id
WHERE phone.num = (SELECT MAX(num)
FROM PHONE_NUMBER);
That one just returned me one row total. I wanted the inner SELECT to run once for each row in EMPLOYEE.
I'm not sure how else to think about this. I basically want my result set to be the number of rows in the EMPLOYEE table and for each row the first matching row in the PHONE_NUMBER table.
Obviously there are all sorts of ways to do this with procedures and scripts and such but I feel like there is a single-query solution in there somewhere...
Any ideas?
I'd use a rank (or dense_rank or row_number depending on how you want to handle ties)
SELECT *
FROM (SELECT emp.*,
phone.num,
rank() over (partition by emp.emp_id
order by phone.num) rnk
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id)
WHERE rnk = 1
will rank the rows in phone for each emp_id by num and return the top row. If there could be two rows for the same emp_id with the same num, rank would assign both a rnk of 1 so you'd get duplicate rows. You could add additional conditions to the order by to break the tie. Or you could use row_number rather than rank to arbitrarily break the tie.
All above answers will work beautifully with the scenario you described.
But if you have some employees which are missing in phone tables, then you need to do a left outer join like below. (I faced similar scenario where I needed isolated parents also)
EMP
---------
emp_id Name
---------
1 AA
2 BB
3 CC
PHONE
----------
emp_id no
1 7555
1 7777
2 5555
select emp.emp_id,ph.no from emp left outer join
(
select emp_id,no,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY emp_id) as rnum
FROM phone) ph
on emp.emp_id = ph.emp_id
where ph.rnum = 1 or ph.rnum is null
Result
EMP_ID NO
1 7555
2 5555
3 (null)
If you want only one phone number, then use row_number():
SELECT e.*, p.num
FROM EMPLOYEE emp JOIN
(SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY emp_id) as seqnum
FROM PHONE_NUMBER p
) p
ON e.emp_id = p.emp_id and seqnum = 1;
Alternatively, you can use aggregation, to get the minimum or maximum value.
This is my solution. Simple but maybe wont scale well for lot of columns.
Sql Fiddle Demo
select e.emp_id, e.name, e.dep, min(p.phone_num)
from
EMPLOYEE e inner join
PHONE_NUMBER p on e.emp_id = p.emp_id
group by e.emp_id, e.name, e.dep
order by e.emp_id;
And this fix the query you try
Sql Fiddle 2
SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id
WHERE phone.num = (SELECT MAX(num)
FROM PHONE_NUMBER p
WHERE p.emp_id = emp.emp_id );

How to check if there exist only one record for a certain Id

How to check if there exist only one record for a certain Id
I have two tables called Tbl_Company and Tbl_Employee
I am fetching employees as follows-
SELECT DISTINCT emp.employee_id
FROM Tbl_Company comp
, Tbl_Employee emp
WHERE
emp.company_id = comp.company_id
AND emp.company_id = 1234;
This query returns exactly one value.
How can I make sure that above query returns exacly one value for any comany_id I enter.
I tried using solutions given in This post with no success.
Is there any simpler way to do this.
this would return one row per company
SELECT comp.companyid, max(emp.employee_id) lastEmployeeID
FROM Tbl_Company comp
, Tbl_Employee emp
WHERE
emp.company_id = comp.company_id
AND emp.company_id = 1234
GROUP BY comp.companyid
the following query is simple and flexible. it will return a list of all employees which are alone in their companies (returned with full employee information).
you can check if a defined company has one lonely employee by enable condition about company or you can check if employee is a lonely employee by enabling employee condition.
SELECT emp.*
FROM Tbl_Company comp
/*, Tbl_Employee emp*/
WHERE (emp.company_id , 1) in (select t.company_id, count(t.employee_id) from Tbl_Company t )
--AND emp.company_id = 1111 /*filter conditions on company_id*/
--AND emp.employee_id = 1234/*filter conditions on employee_id*/;
I have solved this by using ans by #davegreen100 in comment
SELECT comp.companyid, count(distinct emp.employee_id),
FROM Tbl_Company comp
, Tbl_Employee emp
WHERE
emp.company_id = comp.company_id
AND emp.company_id = 1234
GROUP BY comp.companyid
This will give me the count of employees per company

How do get name of max? SQL

I have query:
select max(sumValues)
FROM (select dep.name AS sumName, sum(em.salary) AS sumValues
from EMPLOYEE em
INNER JOIN DEPARTMENT dep ON em.department_id=dep.id
group by dep.id) a
In result I have:
|max numeric|
-----------
200
But I want get in result name (dep.name) of max (max(sumValues)), How can I to do it?
The simplest way is to sort and take the first tuple:
select sumName, sumValues
FROM (
select dep.name AS sumName, sum(em.salary) AS sumValues
from EMPLOYEE em
INNER JOIN DEPARTMENT dep ON em.department_id=dep.id
group by dep.id
) a
ORDER BY sumValues DESC
LIMIT 1;
Otherwise you can use the HAVING clause and a subquery over a CTE term.
(LIMIT is a PostgreSQL extension)