SQL: Employees with more than one department at the same time - sql

I am in need of a query that returns Employee ID, Department, Begin date, End date for the employee who may be registered in more than one department at the same time, meaning:
The Employee1 could be working in
department A: from 01.01.2015 to 31.12.2015 and
department B: from 01.01.2015 to 31.12.2015
(the dates overlap)
OR
department A from 01.01.2015 to 31.12.2015 and
department B from 01.06.2015 to 31.12.2018
(the dates partially overlap)
My tables are something like this
Employee (Employee ID)
Career (Employee ID, Department ID, BeginDate, EndDate)
Department (DepartmentID)
The data that this tables contain are only IDs.
The result should be something like:
EmployeeID Department BeginDate EndDate
1 HR 01.01.2015 31.12.2015
1 ITD 01.01.2015 31.12.2015
2 MR 01.03.2014 31.12.2018
2 HR 01.06.2014 31.12.2016
With the current department column being an ID like (12HDGH4376SHFJ48).
I am not sure how to write this query. I am not sure what to use in the where clause or if it needs sub queries etc. I am a little bit lost with this. Also I work on Oracle. Thank you for any advice.

You can try with something like this:
setup:
create table Employee(EmployeeID number);
create table career(EmployeeID number, DepartmentID number, BeginDate date, EndDate date);
create table department(DepartmentID number, department varchar2(16));
insert into department values (1, 'HR');
insert into department values (2, 'IT');
insert into employee values (20);
insert into employee values (10);
insert into career values (10, 1, to_date('01.01.2015', 'dd.mm.yyyy'), to_date('31.12.2015', 'dd.mm.yyyy'));
insert into career values (10, 2, to_date('01.01.2015', 'dd.mm.yyyy'), to_date('31.12.2015', 'dd.mm.yyyy'));
insert into career values (20, 1, to_date('01.01.2015', 'dd.mm.yyyy'), to_date('31.12.2015', 'dd.mm.yyyy'));
insert into career values (20, 2, to_date('01.06.2015', 'dd.mm.yyyy'), to_date('31.12.2018', 'dd.mm.yyyy'));
query:
select employeeId, departmentId, beginDate, endDate
from (
select employeeId, departmentId, beginDate, endDate,
lag(endDate) over (partition by employeeId order by beginDate) as previousEndDate,
lead(beginDate) over (partition by employeeId order by beginDate) as nextBeginDate
from career
inner join department
using(departmentId)
)
where previousEndDate between beginDate and endDate OR
nextBeginDate between beginDate and endDate
This orders the rows by date and checks, for every row, if it overlaps with the preceeding or the next one, with the same employee

Are you using Oracle, Mysql etc?
Also are you querying 3 tables?
Depending on what you are using you should use a rank to identify the record that is the latest one or the ones that is currently active for the person.
Select
RANK() OVER (partition by Employee ID BY ORDER BY NVL(END_DATE, CURRENT_DATE)DESC) AS RANK.
The rank value will give you the latest record. if you then sub query the whole thing you can then have a where clause like so.
Where a.rank = 1
a being the alias of the sub query.

Related

Pivoting a table with SQL

I have a table with position (junior, senior), salary, and an ID. I have done the following to find the highest salary for each position.
SELECT position, MAX(salary) FROM candidates GROUP BY position;
What I am getting:
How I want it:
I want to transpose the outcome so that 'junior' and 'senior' are the columns without using crosstab. I have looked at many pivot examples but they are done on examples much more complex than mine.
I am not proficient in PostgreSQL, but I believe there is a practical workaround solution since this is a simple table:
SELECT
max(case when position = 'senior' then salary else null end) senior,
max(case when position = 'junior' then salary else null end) junior
FROM payments
It worked with this example:
create table payments (id integer, position varchar(100), salary int);
insert into payments (id, position, salary) values (1, 'junior', 1000);
insert into payments (id, position, salary) values (1, 'junior', 2000);
insert into payments (id, position, salary) values (1, 'junior', 5000);
insert into payments (id, position, salary) values (1, 'junior', 3000);
insert into payments (id, position, salary) values (2, 'senior', 3000);
insert into payments (id, position, salary) values (2, 'senior', 8000);
insert into payments (id, position, salary) values (2, 'senior', 9000);
insert into payments (id, position, salary) values (2, 'senior', 7000);
insert into payments (id, position, salary) values (2, 'senior', 4000);
select
max(case when position = 'junior' then salary else 0 end) junior,
max(case when position = 'senior' then salary else 0 end) senior
from payments;
Here is my attempt at teaching myself crosstab:
CREATE EXTENSION IF NOT EXISTS tablefunc;
select Junior
, Senior
from
(
select *
from crosstab
(
'select 1, position, max(salary)
from candidates
group by position
'
, $$VALUES('Junior'), ('Senior')$$
)
as ct(row_number integer, Junior integer, Senior integer) --I don't know your actual data types, so you will need to update this as needed
) q
Edit: Below is no longer relevant as this appears to be PostgreSQL
Based on your description, it sounds like you probably want a pivot like this:
select q.*
from
(
select position
, salary
from candidates
) q
pivot (
max(salary) for position in ([Junior], [Senior])
) p
This example was made in SQL Server since we don't know DBMS.
It depends on which SQL dialect you are running. It also depends on the complexity of your table. In SQL Server, I believe you can use the solutions provided in this question for relatively simple tables: Efficiently convert rows to columns in sql server

Extract Department each Employee worked for 6 months or more in a year continoulsy

Please below data
Emp. Date. Dept
1. 01/21. Abc
1. 02/15. Xyz
1. 10/19. Cba
2. 01/21. Abc
2. 02/15. Xyz
2. 04. Uvw
Using Oracl Sql i need to extract for each employee in a year if employee worked in department more than or equal to 6 months get that department else current department
Expected result for above data
Emp. Dept
1. Xyz. (Emp workes more than 6 months)
2. Uvw. (No department with 6 months tenure so current department)
You can use the following:
I have created the table and data according to the example.
-- Preparing the data
CREATE TABLE EMPLOYEES (
EMP NUMBER,
DATE1 DATE,
DEPT VARCHAR2(100)
);
INSERT INTO EMPLOYEES VALUES (
1,
TO_DATE('01/21', 'MM/DD'),
'Abc'
);
INSERT INTO EMPLOYEES VALUES (
1,
TO_DATE('02/15', 'MM/DD'),
'Xyz'
);
INSERT INTO EMPLOYEES VALUES (
1,
TO_DATE('10/19', 'MM/DD'),
'Cba'
);
INSERT INTO EMPLOYEES VALUES (
2,
TO_DATE('01/21', 'MM/DD'),
'Abc'
);
INSERT INTO EMPLOYEES VALUES (
2,
TO_DATE('02/15', 'MM/DD'),
'Xyz'
);
INSERT INTO EMPLOYEES VALUES (
2,
TO_DATE('04', 'MM'),
'Uvw'
);
--
-- Final data in the table
SELECT
*
FROM
EMPLOYEES;
-- Your Query
SELECT
EMP,
DEPT
FROM
(
SELECT
EMP,
DEPT,
ROW_NUMBER() OVER(
PARTITION BY EMP
ORDER BY
DURATION DESC NULLS LAST
) AS RN
FROM
(
SELECT
EMP,
DATE1,
DEPT,
MONTHS_BETWEEN(LEAD(DATE1, 1) OVER(
PARTITION BY EMP
ORDER BY
DATE1
), DATE1) AS DURATION
FROM
EMPLOYEES
)
WHERE
DURATION >= 6
OR DURATION IS NULL
)
WHERE
RN = 1;
Output:
Hope, This will be useful to you.
Cheers!!

sql previous row

I have a SQL question, typical :previous row, next row question BUT:
NOT USING rownum,lead or rankover these functions, only select and join,
Table: Student
Fields: Student_ID, Department, Start_Date
ex:
1,C, 2017-01-1
1,B, 2017-07-1
1,A, 2017-12-1
Expected Output:
Student_ID, Department, Start_Date, End_Date
ex:
1,C, 2017-01-1, 2017-07-01
1,B, 2017-07-1,2017-12-01
1,A, 2017-12-1, ...
End_Date is the start Date of the next record for the student ID
You could try this:
Data
create table student (
Student_ID int,
Department char(1),
Start_Date date
);
insert into student values (1, 'A', '2017-01-01');
insert into student values (1, 'B', '2017-01-01');
insert into student values (1, 'C', '2017-12-31');
SQL Server
select
student_id,
department,
start_date,
(select top 1 start_date
from student
where student_id = s.student_id
) as end_date
from student s
order by student_id, department;
Example: http://rextester.com/HLL58959
PostgreSQL and MySQL and SQLite
select
student_id,
department,
start_date,
(select start_date
from student
where student_id = s.student_id
limit 1) as end_date
from student s
order by student_id, department;
Example: http://rextester.com/XWUAZ90711

SQL: How to get the row when max is on more than 1 column

I am working with Microsoft SQL Server 2008 R2.
I have a table named employee:
create table employee (
employee_id bigint not null primary key,
first_name varchar(50) not null,
middle_name varchar(50) null,
last_name varchar(50) not null
)
I have a table named eligibility. It has a FK to employee table. It has a unique key comprise of 3 columns: employee_id + effective_date + sequence_number.
create table eligibility (
eligibility_id bigint not null primary key,
employee_id bigint not null foreign key references employee (employee_id),
effective_date date not null,
sequence_number int not null,
value varchar(20) not null,
constraint UK_eligibility unique (employee_id, effective_date, sequence_number)
)
I have 1 row in employee table with employee_id = 1001:
insert into employee (employee_id, first_name, middle_name, last_name) values (1001, 'A', 'B', 'C')
I have 4 rows in eligibility table for the same employee_id:
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (1, 1001, '2016-04-13', 1, 'NS')
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (2, 1001, '2016-05-25', 1, 'EX')
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (3, 1001, '2016-05-25', 2, 'VR')
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (4, 1001, '2016-06-05', 1, 'LS')
From the eligibility table, for a given date I want to get the row with the max (effective_date + sequence_number) combination which is less than or equal to that given date.
Examples:
For 2016-04-30 date I would want the row with eligibility_id = 1.
For 2016-05-30 date I would want the row with eligibility_id = 3.
For 2016-06-30 date I would want the row with eligibility_id = 4.
I have wrote the query to get the desired results. This is the query for 2016-05-30 date:
select * from eligibility e
where
e.effective_date = (select max(e1.effective_date)
from eligibility e1
where e1.employee_id = e.employee_id and
e1.effective_date <= '2016-05-30') AND
e.sequence_number = (select max(e2.sequence_number)
from eligibility e2
where e2.employee_id = e.employee_id and
e2.effective_date = e.effective_date)
The query is ok but I want to try write it in some different way to get the same results. What other way you would recommend?
Hmmm, I would use row_number():
select e.*
from (select e.*,
row_number() over (partition by employee_id order by effective_date desc, sequence_number desc
) as seqnum
from eligibility e
) e
where seqnum = 1;
This looks to me like TOP-1 with ties:
SELECT TOP 1 WITH TIES *
FROM eligibility e
WHERE e.effective_date <= '2016-05-30'
ORDER BY e.effective_date DESC, sequence_number DESC

SQL Nested Aggregate Query- How do I get the number of customers per employee?

The following is the prompt I need to answer:
List the number of customers for each employee. Include employee's name and number of
customers labeled appropriately, listing the employee with the most customers first.
The following is my EMPLOYEES table:
create table EMPLOYEES
(EmpID char(4) unique Not null,
Ename varchar(10),
Job varchar(9),
MGR char(4),
Hiredate date,
Salary decimal(7,2),
Comm decimal(7,2),
DeptNo char(2) not null,
Primary key(EmpID),
Foreign key(DeptNo) REFERENCES DEPARTMENTS(DeptNo));
insert into EMPLOYEES values (7839,'King','President',null,'17-Nov-11',5000,null,10);
insert into EMPLOYEES values (7698,'Blake','Manager',7839,'01-May-11',2850,null,30);
insert into EMPLOYEES values (7782,'Clark','Manager',7839,'02-Jun-11',2450,null,10);
insert into EMPLOYEES values (7566,'Jones','Manager',7839,'02-Apr-11',2975,null,20);
insert into EMPLOYEES values (7654,'Martin','Salesman',7698,'28-Feb-12',1250,1400,30);
insert into EMPLOYEES values (7499,'Allen','Salesman',7698,'20-Feb-11',1600,300,30);
insert into EMPLOYEES values (7844,'Turner','Salesman',7698,'08-Sep-11',1500,0,30);
insert into EMPLOYEES values (7900,'James','Clerk',7698,'22-Feb-12',950,null,30);
insert into EMPLOYEES values (7521,'Ward','Salesman',7698,'22-Feb-12',1250,500,30);
insert into EMPLOYEES values (7902,'Ford','Analyst',7566,'03-Dec-11',3000,null,20);
insert into EMPLOYEES values (7369,'Smith','Clerk',7902,'17-Dec-10',800,null,20);
insert into EMPLOYEES values (7788,'Scott','Analyst',7566,'09-Dec-12',3000,null,20);
insert into EMPLOYEES values (7876,'Adams','Clerk',7788,'12-Jan-10',1100,null,20);
insert into EMPLOYEES values (7934,'Miller','Clerk',7782,'23-Jan-12',1300,null,10);
The following is my CUSTOMERS table:
create table CUSTOMERS
(CustID char(6) unique Not null,
Name varchar(45),
Address varchar(40),
City varchar(30),
State varchar(2),
Zip varchar(9),
AreaCode char(3),
Phone varchar (9),
RepID char(4) not null,
CreditLimit decimal(9,2),
Primary key(CustID),
Foreign key(RepID) References EMPLOYEES(EmpID));
insert into CUSTOMERS values (100,'Jocksports','345 Viewridge','Belmont','CA','96711',415,'598-6609',7844,5000);
insert into CUSTOMERS values (101,'TKB Sport Shop','490 Boli Rd.','Redwood City','CA','94061',415,'368-1223',7521,10000);
insert into CUSTOMERS values (102,'Vollyrite','9722 Hamilton','Burlingame','CA','95133',415,'644-3341',7654,7000);
insert into CUSTOMERS values (103,'Just Tennis','Hillview Mall','Burlingame','CA','97544',415,'677-9312',7521,3000);
insert into CUSTOMERS values (104,'Every Mountain','574 Surry Rd.','Cupertino','CA','93301',408,'996-2323',7499,10000);
insert into CUSTOMERS values (105,'K + T Sports','3476 El Paseo','Santa Clara','CA','91003',408,'376-9966',7844,5000);
insert into CUSTOMERS values (106,'Shape Up','908 Sequoia','Palo Alto','CA','94301',415,'364-9777',7521,6000);
insert into CUSTOMERS values (107,'Womens Sports','Valco Village','Sunnyvale','CA','93301',408,'967-4398',7499,10000);
insert into CUSTOMERS values (108,'North Woods Fitness Supply Center','98 Lone Pine Way','Hibbing','MN','55649',612,'566-9123',7844,8000);
The following is my query:
select ename, empId
from EMPLOYEES
where EmpID in
(select count(repid) as NumberOfCustomers
from CUSTOMERS
group by RepID);
Why is my query not working?
I know I want to match the empid from EMPLOYEES to the repID in CUSTOMERS and then COUNT how many times the rep ID shows up in CUSTOMERS. The only reason I need the EMPLOYEES table is to out put Ename. Im confused about the syntaxt I need to use because I need to output the count of RepID in the CUSTOMERS table
You can also use Common Table Expression if you are using SQL Server 2005 and above.
;WITH CTE
AS
(
SELECT RepID, COUNT(*) AS CNT
FROM CUSTOMERS
GROUP BY REPID
)
SELECT E.Ename, E.EmpID, ISNULL(C.CNT, 0)
FROM EMPLOYEES E
LEFT OUTER JOIN CTE C ON C.RepID = E.EmpID
DEMO
SELECT *
FROM employees e
CROSS APPLY
(
SELECT COUNT(*)
FROM customers c
WHERE c.repId = e.empId
) cc (cnt)
ORDER BY
cnt DESC
See this on SQLFiddle
The same with a subquery:
SELECT *,
(
SELECT COUNT(*)
FROM customers c
WHERE c.repId = e.empId
) cnt
FROM employees e
ORDER BY
cnt DESC
Your inner query only has a count(repid), which is a count, not the actual EmployeeID that your outer query requires in where EmpID in (xxx). Not sure why're you want a count in your inner query, because you'll lose it in the final result, but this should work:
select ename, empId
from EMPLOYEES
where EmpID in (
select repID
from CUSTOMERS);
What about this? or did i misunderstand
SELECT ename, count(*) as NumCustomers
FROM Customers INNER JOIN
Employees ON customers.repid = employees.empid GROUP BY repid,ename
ORDER BY numCustomers DESC