I have table employee and table family. Let's say that employee has name column and salary column.
Then I have to calculate their salary: 3% to salary of employee who have family and 2% to all who don't have family.
Do you have any idea how to do this? I know that I have to use exist but I don't know how to calculate the salary.
This table employee:
employeeID int,
Name varchar(10),
PhoneNumber varchar(20),
ICNumber varchar(15),
Salary decimal(5,2),
primary key(employeeId));
This is table family
familyId int,
name varchar(20),
family varchar(20),
address varchar(25),
phoneNumber varchar(20),
employeeID int,
primary key (employeeID),
FOREIGN KEY (employeeID) REFERENCES Employee(employeeID))
I'm assuming you wanted to select the +3% or +2% value rather than update?
SELECT
employeeid,
salary,
salary * loading_factor AS loading
FROM (
SELECT
employeeid,
salary,
CASE
WHEN EXISTS SELECT 1 FROM family f WHERE f.employeeID = e.employeeID THEN 0.03
ELSE 0.02
END AS loading_factor
FROM employee e
)
You can do it without the sub-select, but I thought it would read easier. If you want the total new salary amount rather than the increment, change the 0.02 / 0.03 to 1.02 / 1.03.
The SQL EXISTS condition is used in combination with a subquery and is considered to be met, if the subquery returns at least one row. It can be used in a SELECT, INSERT, UPDATE, or DELETE statement.
http://www.techonthenet.com/sql/exists.php
So let's see how we can use that to determine whether to update a particular salary by 3% or 2%.
For employees with a family:
UPDATE Employee
SET Salary = 1.03 * Salary
WHERE EXISTS (
SELECT 1 FROM Family WHERE Employee.employeeId = Family.employeeId
)
I'll leave the other case as an exercise to the reader (hint).
UPDATE
If you just want to select what the new salary should be
SELECT employeeId, 1.03 * Salary As NewSalary FROM Employee
WHERE EXISTS (
SELECT 1 FROM Family WHERE Employee.employeeId = Family.employeeId
)
Personally, I try not to use exists or not exists clauses when possible. In your case, you can get your desired result with a left outer join:
select
emp.name,
emp.salary,
case when fam.employeeID is null
then 0.02
else 0.03
end * emp.salary salary_adjusted
from
employee emp left join
family fam on emp.employeeID = fam.employeeID;
Related
I barely just started on database languages, so I do not know anything about JOIN, PARTITION, ETC. but this is what I have so far.
What I tried:
SELECT MIN(SALARY) AS "MIN SALARY", WORKING.DID, ENAME
FROM DEPARTMENT, EMPLOYEE, WORKING
WHERE WORKING.EID = EMPLOYEE.EID
GROUP BY WORKING.DID, ENAME;
Result:
MIN SALARY DID ENAME
---------- --- ------
10000 101 Dustin
15000 102 Bob
20000 102 David
10000 102 Dustin
10000 103 Alex
8000 103 Alice
7000 103 Mike
What I want:
MIN SALARY DID ENAME
---------- --- ------
10000 101 Dustin
10000 102 Dustin
7000 103 Mike
Table structures:
CREATE TABLE EMPLOYEE (
EID INTEGER NOT NULL,
ENAME VARCHAR(25),
SALARY DECIMAL,
PRIMARY KEY (EID)
);
CREATE TABLE WORKING (
EID INTEGER NOT NULL,
DID INTEGER NOT NULL,
STIME DATE,
FOREIGN KEY (EID) REFERENCES EMPLOYEE (EID),
FOREIGN KEY (DID) REFERENCES DEPARTMENT (DID),
PRIMARY KEY (EID, DID)
);
CREATE TABLE DEPARTMENT (
DID INTEGER NOT NULL,
DNAME VARCHAR(10),
DADDRESS VARCHAR(20),
PRIMARY KEY (DID)
);
ER Diagram
You can prefer using online analytical processing(OLAP) functions such as DENSE_RANK() to get the desired result easily
SELECT Q.SALARY, Q.DID, Q.ENAME
FROM
(
SELECT E.SALARY, W.DID, E.ENAME,
DENSE_RANK() OVER (PARTITION BY W.DID ORDER BY E.SALARY) AS DR
FROM WORKING W
JOIN DEPARTMENT D
ON D.DID = W.EID
JOIN EMPLOYEE E
ON E.EID = W.EID
) Q
WHERE DR = 1
in which explicit JOIN syntax, the syntax having comma-seperated tables is considered as old-deprecated one , is used. Using (INNER)JOIN is enough for your case, whereas there may be cases needing OUTER (LEFT-RIGHT or FULL) JOINs.
Within an analytic function, PARTITION Clause is used to express the GROUPING BY criteira. Based on the ascending ORDER for the salary, we'll get the minimum ones to be filtered out through the results coming from the DENSE_RANK() function with values equal to one.
By using this method, you can get all the employees with the minimum salaries even there are more than one person for each department.
Moreover, better to alias the tables by their name's first character(or by two or three chars contained within their names depending on the common chars. for the first letters) in order to qualify the columns within the query rather than using whole names.
There is no need to use the DEPARTMENT table in the statement.
While using OLAP functions is preferable way and generally provides better performance, there is another method not using OLAP functions based on rejoining base tables to a sub-select with grouped result, where we get department minimum salary. We rejoin these department minimums back to WORKING & EMPLOYEE picking up employees with the department minimum salary only.
SELECT G.DID, G.SALARY, E.ENAME
FROM
(
SELECT W.DID, MIN(E.SALARY) SALARY
FROM WORKING W
JOIN EMPLOYEE E ON E.EID = W.EID
GROUP BY W.DID
) G
JOIN WORKING W ON W.DID = G.DID
JOIN EMPLOYEE E ON E.EID = W.EID AND E.SALARY = G.SALARY;
I need to create a view in PostgreSQL 9.4 about this table:
CREATE TABLE DOCTOR (
Doc_Number INTEGER,
Name VARCHAR(50) NOT NULL,
Specialty VARCHAR(50) NOT NULL,
Address VARCHAR(50) NOT NULL,
City VARCHAR(30) NOT NULL,
Phone VARCHAR(10) NOT NULL,
Salary DECIMAL(8,2) NOT NULL,
DNI VARCHAR(10) NOT NULL,
CONSTRAINT pk_Doctor PRIMARY KEY (Doc_Number)
);
The view will show the rank of the doctors with highest salary for each specialty, I tried this code but it shows all of the doctors fro each specialty:
CREATE VIEW top_specialty_doctors
AS (Select MAX(Salary), name, specialty from DOCTOR
where specialty = 'family and community'
or specialty = 'psychiatry'
or specialty = 'Rheumatology'
group by name, salary, specialty);
How can I do for the view shows only the doctor with highest salary for each specialty.
DISTINCT ON is a simple Postgres specific technique to get one winner per group. Details:
Select first row in each GROUP BY group?
CREATE VIEW top_specialty_doctors AS
SELECT DISTINCT ON (specialty)
salary, name, specialty
FROM doctor
WHERE specialty IN ('family and community', 'psychiatry', 'Rheumatology')
ORDER BY specialty, salary DESC, doc_number -- as tiebreaker
And you do not need parentheses around the query for CREATE VIEW.
If multiple docs tie for the highest salary, the one with the smallest doc_number is selected.
If salary can be NULL, use DESC NULLS LAST:
PostgreSQL sort by datetime asc, null first?
For big tables and certain data distributions other query techniques are superior:
Optimize GROUP BY query to retrieve latest record per user
Here's a query that shows the best doctor by salary for each of the specialties:
with specialty_ranks as (
select
Salary, name, specialty,
rank() over (
partition by specialty
order by salary desc
) as rank
from DOCTOR
where specialty in ('family and community', 'psychiatry', 'Rheumatology')
)
select specialty, name, salary
from specialty_ranks
where rank = 1;
The query uses a CTE and the RANK() window function to do the job. You might want to read their docs if you haven't used them before.
Without using Common Table Expressions or analytics, you can use an inline view/virtual table:
Create View top_specialty_doctors as
Select m.MaxSalary, d.Name, d.Specialty
From Doctor d
Join( -- Expose largest salary of each specialty
Select Specialty, Max( Salary) as MaxSalary
From Doctor
Group by Specialty
) as m
on m.Specialty = d.Specialty
and m.MaxSalary = d.Salary
Where specialty in( 'family and community', 'psychiatry', 'Rheumatology' );
Using a CTE instead of the inline view makes the query more readable and allows the query optimizer to turn out better performance (usually). They are really easy to learn.
I have a table named Employee which has the following columns:
ID(primary key) of type int
Name of type varchar(255)
Designation of type varchar(50)
Salary of type int
I want to write a sub-query, which will give me the names of the employees who have greater salary than ANY employee of the designation 'Junior Officer'.
Here's what I have tried but was unsuccessful:
SELECT Name
FROM Employee
WHERE Salary>
(SELECT Salary FROM Employee WHERE Designation = 'Junior Officer');
Try this.
SELECT Name
FROM Employee
WHERE Salary>
(SELECT max(Salary) FROM Employee WHERE Designation = 'Junior Officer');
Because it is urgent, I'm writing you this without testing it. Try this:
SELECT Name
FROM Employee
WHERE Salary >
(SELECT MAX(Salary) FROM Employee WHERE Designation = 'Junior Officer');
I have an 'employee' table with
create table employee
(
e_number int,
e_name varchar(20),
salary money,
hire_date date
)
Now I want to display only the name of the employees who have the same name but different salary.
I tried select e_name,count(*) from employee group by e_name having count(*)>1;
but cannot combine it with "the same salary" section. Any help?
This assumes that you want the names of both of the people listed:
SELECT e1.e_name
FROM employee e1, employee e2
WHERE e1.e_name = e2.e_name
AND e1.salary <> e2.salary;
If you only want each name listed once, you would use a SELECT DISTINCT instead of the SELECT.
If your goal is to express this in the having clause:
Select name
from employee
group by name
having
count(*) > 1
and min(salary) != max(salary)
order by name
SELECT employee1.e_name, employee1.Salary, Employee2.Salary
FROM Employee employee1
JOIN Employee employee2
on employee1.name = employee2.name
AND Employee1.Salary <> Employee2.Salary
AND Employee1.E_Number <> employee2.E_Number
Basically get every employee, join it to every other employee via name, where the employee number is different (so don't join to yourself) and salary is different.
You probably don't need to check that employee number is different because 1 employee can only have 1 salary in your table design
Use a join, but importantly use a greater-than comparison, rather than a not-equals, to avoid duplicates:
SELECT e1.e_name as name1, e2.e_name as name2
FROM employee e1
JOIN employee e2 ON e1.e_name = e2.e_name
AND e1.salary > e2.salary;
Note also that the ON condition contains the salary comparison. It is a common misconception that the join on condition may only contain key-related comparisons. Doing this can have significant performance benefits, especially when further joins are made, because the ON condition is executed as the rows are joined - which discards non-matches immediately, whereas WHERE conditions are executed as a filter on the entire result set of the joins.
You just want the count of distinct salaries, not the count of all records.
select e_name,count(distinct salary)
from employee
group by e_name
having count(distinct salary)>1
(Drop the count in the select if unneeded - included since it was in your example)
First filter salary not double (not in), then grouping by e_name having count > 1
SELECT A.e_name
FROM employee A
WHERE A.salary NOT IN (SELECT salary FROM employee WHERE id != A.id)
GROUP BY A.e_name
HAVING COUNT(A.e_name) > 1
I have a table named employee_salary, with three columns: empsal_id, empsal_name and empsal_sal. The data is as follows:
empsal_id empsal_name empsal_sal
1 dilip 14000
2 santosh 20000
3 amit 32000
4 dilip 22000
5 amit 38000
6 santosh 25000
7 dilip 30000
The empsal_id is an Identity column with a Seed and an Increment of 1, and is the Primary Key. I want to return the name and current salary of each employee. Salary can decrease as well as increase, so current does not necessarily mean highest.
So I need the following output:
empname emp_sal
dilip 30000
amit 38000
santosh 25000
I am using Microsoft SQL Server, and I have to do this in a single query.
This query will return each employee, along with their highest salary:
SELECT
empsal_name, MAX(empsal_sal)
FROM
employee
GROUP BY
empsal_name
This query will return each employee, along with their current salary (i.e. the salary with the highest empsal_id:
SELECT
empsal_name, empsal_sal
FROM
employee e1
WHERE
empsal_id =
(SELECT MAX(empsal_id)
FROM employee e2
WHERE e1.empsal_name=e2.empsal_name)
Personally, I think you would be better off using an effective date column (e.g. empsal_effectivedate) to determine which record is the most current, so this query will return each employee, along with their current salary (i.e. the salary with the most recent empsal_effectivedate), assuming there is an empsal_effectivedate field:
SELECT
empsal_name, empsal_sal
FROM
employee e1
WHERE
empsal_effectivedate =
(SELECT MAX(empsal_effectivedate)
FROM employee e2
WHERE e1.empsal_name=e2.empsal_name)
Assuming there is also an ID in the table and also assuming that the larger this ID is the most recent the salary is for that employee you can do
SELECT DISTINCT
e.empname,
(SELECT TOP 1
emp_sal
FROM
employee s
WHERE
s.empname = e.empname
ORDER BY
recid DESC) AS emp_sal
FROM
employee e
(also assuming that empname is unique for an employee)
because of the many assumptions though : you should probably post all the columns of the table and what they mean ..
SELECT empname, MAX(emp_sal)
FROM employee
GROUP BY empname
UPDATED:
SELECT DISTINCT EmpA.empname,
EmpA.emp_sal
FROM Employee AS EmpA
INNER JOIN ( SELECT EmpName, MAX(recID) AS recid
FROM Employee
GROUP BY EmpName
) AS EmpB ON EmpA.recid = EmpB.recid;
You need a date field to determine the latest inserted row . In case if the table is linked to some other table which has date column in it .Then its pretty easy to fetch the current data .
For Example
Employee Table
{
EmpName varchar(30) PK,
EmpAddress varchar(255) ,
Company varchar(30),
CurrentTimeStamp Datetime
}
Salary Table
{
EmpName varchar(30) FK,
EmpSalary int
}
To get the Latest record use the CTE function
With LatestSal(EmpName ,EmpSalary)
AS
(
Select row_number() over (PARTITION BY b.[EmpName], order by CurrentTimestamp DESC) as seq
b.EmpName,b.EmpSalary
From Employee as a,
Salary as b
on a.[EmpName]=b.[EmpName]
)
Select EmpName,EmpSalary
from LatestSal
where seq=1