Let's say these are the 2 tables:
workers[name, dep_id, salary]
and
department[id, name, city]
How can I achieve to raise the salary of those who work in a particular city?
I'd only know how to do it if the city was in the workers table (then it'd be just UPDATE workers SET salary = salary * 1.1 WHERE city = 'X'), but it doesn't work when the salary and city are in different tables.
You can use this query:
UPDATE workers w
SET salary = salary * 1.1
WHERE EXISTS (SELECT *
FROM departments d
WHERE d.id = w.dep_id
AND d.city = 'X')
You can use also other queries, depending on your specific system.
UPDATE w
SET w.salary = w.salary*1.1
FROM workers w
LEFT JOIN department d
ON d.id = w.dept_id
WHERE d.city = 'denver'
I believe this will work. Left joining to department will give you the extra columns you're looking for for each row in workers.
UPDATE
w
SET
w.salary = w.salary * 1.1
FROM
workers w
INNER JOIN
departments d
ON
w.dep_id = d.id
WHERE
d.city = 'X'
Best and most safe way to do any data update is to first select rows which you want to update.
So First step will be
SELECT (*) // Select all
FROM
workers w // From workers
INNER JOIN
departments d // and departments
ON
w.dep_id = d.id // where workers dep_id vakue is in
// departments id column
// INNER JOIN get ONLY that data
WHERE
d.city = 'X' // and filter by city name
now you can replace select part with
UPDATE
w // Update table which name or alias is w
SET
w.salary = w.salary * 1.1 / set salary to current salary * 1.1
Related
I have 2 tasks:
1. FIRST TASK
Show first_name, last_name (from employees), job_title, employee_id (from jobs) start_date, end_date (from job_history)
My idea:
SELECT s.employee_id
, first_name
, last_name
, job_title
, employee_id
, start_date
, end_date
FROM employees
INNER JOIN jobs hp
on s.employee_id = hp.employee_id
INNER JOIN job_history
on hp.jobs = h.jobs
I know it doesn't work. I'm receiving: "HP"."EMPLOYEE_ID": invalid identifier
What does it mean "on s.employee_id = hp.employee_id". Maybe I should write sthg else instead of this.
2. SECOND TASK
Show department_name (from departments), average and max salary for each department (those data are from employees) and how many employees are working in those departments (from employees). Choose only departments with more than 1 person. The result round to 2 decimal places.
I have the pieces, but i don't know to connect it
My idea:
SELECT department_name,average(salary),max(salary),count(employees_id)
FROM employees
INNER JOIN departments
on employees_id = departments_id
HAVING count(department) > 1
SELECT ROUND(average(salary),2) from employees
I modified your queries a bit by improving table aliasing. Hopefully, if the right columns are present in the tables as you say, it should work:
SELECT s.employee_id, s.first_name, s.last_name,
hp.job_title, hp.employee_id,
h.start_date, h.end_date
FROM employees s
INNER JOIN jobs hp
on s.employee_id = hp.employee_id
INNER JOIN job_history h
on hp.jobs = h.jobs;
When we say on s.employee_id = hp.employee_id it means that if, for example, there is an employee_id = 1234 present in both the tables employees and jobs, then SQL will bring all the columns from both the tables in the same line that corresponds to employee_id = 1234. You can now pick different columns in the SELECT clause as if they are in the same/single table(which was not the case before joining). This is the main logic behind SQL joins.
As to your 2nd task, try the below query. I made some modifications in aggregation by introducing COUNT(DISTINCT s.employees_id). If the same employees_id is present twice for some reason, you still want to count that as one person.
SELECT d.department_name, avg(s.salary), max(s.salary), count(distinct s.employees_id)
FROM employees s
INNER JOIN departments d
on e.employees_id = d.departments_id
GROUP BY d.department_name
HAVING COUNT(DISTINCT s.employees_id) > 1;
Let me know if there is still any issue. Hopefully, this works.
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 */ -- ??
);
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.
I've tried the following commands:
UPDATE staff SET salary = (salary * 1.1)
where branchno = (select branchno from branch where city = 'London');
update salary from staff s join branch b on s.branchno = b.branchno
where b.city = 'London' set salary = salary * 1.1;
However, I get this back as an error:
> ERROR: more than one row returned by a subquery used as an expression
Any ideas? essentially I want to update all members of staff's salaries by 10% that live in London, but I must join the Staff and Branch table to get the branches location.
Your query transformed to proper join syntax:
UPDATE staff s
SET salary = (salary * 1.1)
FROM branch b
WHERE b.city = 'London'
AND s.branchno = b.branchno;
This avoids the reported error. The manual has more on UPDATE.
so my main goal with this query is to select all departments and employees under a manager named Mario Souza.
SELECT d.deptname,e.name FROM employee e JOIN department d ON e.dept = d.deptnum WHERE idmanager IN(
SELECT id FROM employee WHERE name = 'Mario Souza'
) AND manager IN(
SELECT id FROM employee WHERE name = 'Mario Souza'
)
And it's working, but is there a way I could store the first IN result so I could use it later after the AND operator?
You can use EXISTS to match on multiple columns.
WHERE EXISTS
(
SELECT *
FROM employee AS manager
WHERE manager.name = 'Mario Souza'
AND manager.id = e.idmanager
AND manager.id = d.manager
)
You could use a JOIN with employee table. Simply put both manager and idmanager in ON clause.