Query required for given case - sql

I have two tables one with name, Employee_Info which contains Employee_no, Name and Department and other table with name Attendance which contains Employee_no and Attendance_Date. Attendance table contains data of those employees who have marked their attendance.
Now I want a query which can help me select those employees who are absent on a specific date in the format as below:
Employee_no Department Absent_on
10160100000 XYZ 06/25/2021
Kindly help!!

Here's one option. Read comments within code (sample data in lines #1 - 14; query you might want to use begins at line #15).
SQL> with
2 -- sample data
3 employee_info (employee_no, name, department) as
4 (select 1, 'Little', 10 from dual union all
5 select 2, 'Foot' , 20 from dual
6 ),
7 attendance (employee_no, attendance_date) as
8 (select 1, date '2021-09-01' from dual union all
9 select 1, date '2021-09-02' from dual union all
10 select 1, date '2021-09-03' from dual union all
11 -- employee 2 was absent on 2021-09-02
12 select 2, date '2021-09-01' from dual union all
13 select 2, date '2021-09-03' from dual
14 ),
15 missing_attendance_info as
16 -- list of employees who were absent
17 (select e.employee_no, a.attendance_date
18 from attendance a cross join employee_info e
19 minus
20 select b.employee_no, b.attendance_date
21 from attendance b
22 )
23 -- finally, join employee_info with employees who were absent
24 select m.employee_no, e.name, e.department, m.attendance_date
25 from missing_attendance_info m join employee_info e
26 on e.employee_no = m.employee_no;
EMPLOYEE_NO NAME DEPARTMENT ATTENDANCE
----------- ------ ---------- ----------
2 Foot 20 2021-09-02
SQL>

Well you can do it like this,
SELECT EMP.EMPLOYEE_NO AS EMPLOYEE_NO, EMP.DEPARTMENT AS DEPARTMENT, :IN_DATE AS ABSENT_ON
FROM EMPLOYEE_INFO EMP
WHERE
EMP.EMPLOYEE_NO NOT IN (SELECT EMP.EMPLOYEE_NO
FROM ATTENDANCE ATT
LEFT JOIN EMPLOYEE_INFO EMP
ON ATT.EMPLOYEE_NO = EMP.EMPLOYEE_NO
WHERE
TO_CHAR(ATT.ATTENDANCE_DATE, 'MM/DD/YYYY') = :IN_DATE);

Related

List the branch that monthly pays the most in salaries

I have this table, the expected output should be B003 since it's pays 54,000
STAFF
SALARY
BRAN
SL21
30000
B005
SG37
12000
B003
SG14
18000
B003
SA9
9000
B007
SG5
24000
B003
SL41
9000
B005
So far I only have this subquery, which isn't working how I expected.
SELECT BRANCHNO
FROM STAFF
WHERE (SALARY) IN (SELECT MAX(SUM(SALARY))
FROM STAFF
GROUP BY BRANCHNO);
This works but I want a subquery that returns the branchno
SELECT MAX(SUM(SALARY))
FROM STAFF
GROUP BY BRANCHNO;
select BRANCHNO max(sum_sal)
from (SELECT BRANCHNO, SUM(SALARY) sum_sal
FROM STAFF
GROUP BY BRANCHNO) q1
group by BRANCHNO ;
The column used to group the rows can be displayed. So, add BRANCHNO to your select clause.
One option is to use rank analytic function which ranks branches by sum of their salaries in descending order; you'd then return the one(s) that rank as the highest (rnk = 1).
Sample data:
SQL> with staff (staff, salary, bran) as
2 (select 'SL21', 30000, 'B005' from dual union all
3 select 'SG37', 12000, 'B003' from dual union all
4 select 'SG14', 18000, 'B003' from dual union all
5 select 'SA9' , 9000, 'B007' from dual union all
6 select 'SG5' , 24000, 'B003' from dual union all
7 select 'SL41', 9000, 'B005' from dual
8 )
Query:
9 select bran
10 from (select bran, rank() over (order by sum(salary) desc) rnk
11 from staff
12 group by bran
13 )
14 where rnk = 1;
BRAN
----
B003
SQL>

How to get min and max from 2 tables in SQL

I am Trying to get start date from min ID (ID=1) and end date from max ID (ID=3) but i am not sure how i can retrieve. Following is my data -
Table1 and Table2 are source table. I am trying to get output like 3rd table.
My requirement is get start date from first record of ID and End Date from last record of ID, we can recognize first and and last record with the help of ID field. If ID is min means first record and ID is max then last record
Please help me!
Here's one option; presuming you use Oracle (regarding you use Oracle SQL Developer), the x inline view selects
start_date which belongs to name with the lowest ID column value for that name (i.e. first_value partition by name order by id)
end_date which belongs to name with the highest ID column value for that name (i.e. first_value partition by name order by id DESC)
SQL> with
2 -- sample data
3 t1 (pid, name) as
4 (select 123, 'xyz' from dual union all
5 select 234, 'pqr' from dual
6 ),
7 t2 (id, name, start_date, end_date) as
8 (select 1, 'xyz', date '2020-01-01', date '2020-07-20' from dual union all
9 select 2, 'xyz', date '2020-02-01', date '2020-05-30' from dual union all
10 select 3, 'xyz', date '2020-06-30', date '2020-07-30' from dual union all
11 --
12 select 1, 'pqr', date '2020-04-30', date '2020-09-30' from dual union all
13 select 2, 'pqr', date '2020-05-30', date '2020-09-30' from dual union all
14 select 3, 'pqr', date '2020-06-30', date '2020-07-01' from dual
15 )
16 select a.pid,
17 x.name,
18 max(x.start_date) start_date,
19 max(x.end_date) end_date
20 from t1 a join
21 (
22 -- start_date: always for the lowest T2.ID value row
23 -- end_date : always for the highest T2.ID value row
24 select b.name,
25 first_value(b.start_date) over (partition by b.name order by b.id ) start_date,
26 first_value(b.end_date) over (partition by b.name order by b.id desc) end_date
27 from t2 b
28 ) x
29 on a.name = x.name
30 group by a.pid,
31 x.name
32 order by a.pid;
PID NAME START_DATE END_DATE
---------- ---- ---------- ----------
123 xyz 01/01/2020 07/30/2020
234 pqr 04/30/2020 07/01/2020
SQL>

Split date range into weeks in sql

Given a table called Project, I need the list of team_id's who won at least an award every week in last 3 months
launch_date team_id project_name
2019-01-01 123 A
2019-01-01 345 B
2019-01-01 357 C
2019-01-09 123 D
2019-01-08 345 E
2019-01-21 123 F
project_name award
A Y
B N
C Y
D Y
E N
F Y
last 3 months can be achieved with below where condition but how do i split the launch_date into weekly intervals
where launch_date >= sysdate - 90
With the given data, answer should be team id 123
In your sample data, You have only given 21 days of data instead of 3 months.
You can find out the total number of weeks and their week starting date which can then be compared with your table data to check if an award is won by the team for each week as follows:
SQL> --SAMPLE DATA
SQL> with teams (launch_date, team_id, project_name)
2 as
3 (SELECT DATE'2019-01-01', 123, 'A' FROM DUAL UNION ALL
4 SELECT DATE'2019-01-01', 345, 'B' FROM DUAL UNION ALL
5 SELECT DATE'2019-01-01', 357, 'C' FROM DUAL UNION ALL
6 SELECT DATE'2019-01-09', 123, 'D' FROM DUAL UNION ALL
7 SELECT DATE'2019-01-08', 345, 'E' FROM DUAL UNION ALL
8 SELECT DATE'2019-01-21', 123, 'F' FROM DUAL),
9 AWARDS(project_name, award)
10 AS
11 (SELECT 'A','Y' FROM DUAL UNION ALL
12 SELECT 'B','N' FROM DUAL UNION ALL
13 SELECT 'C','Y' FROM DUAL UNION ALL
14 SELECT 'D','Y' FROM DUAL UNION ALL
15 SELECT 'E','N' FROM DUAL UNION ALL
16 SELECT 'F','Y' FROM DUAL),
17 -- YOUR QUERY START FROM HERE
18 -- WITH
19 WKS(DT) AS
20 (SELECT DISTINCT TRUNC(DATE '2019-01-21' - LEVEL + 1, 'W')
21 FROM DUAL CONNECT BY LEVEL <= 21
22 )
23 SELECT T.TEAM_ID
24 FROM WKS W
25 LEFT JOIN TEAMS T ON W.DT = TRUNC(T.LAUNCH_DATE, 'W')
26 LEFT JOIN AWARDS A ON A.PROJECT_NAME = T.PROJECT_NAME
27 WHERE A.AWARD = 'Y'
28 GROUP BY T.TEAM_ID
29 HAVING COUNT(1) = ( SELECT COUNT(1) FROM WKS);
TEAM_ID
----------
123
SQL>
In WKS cte for 3 months data, You need to replace the
WKS(DT) AS
(SELECT DISTINCT TRUNC(DATE '2019-01-21' - LEVEL + 1, 'W')
FROM DUAL CONNECT BY LEVEL <= 21
)
with
WKS(DT) AS
( SELECT DISTINCT TRUNC(sysdate - LEVEL + 1, 'W')
FROM DUAL CONNECT BY LEVEL <= trunc(sysdate) - add_months(trunc(sysdate), -3
)

Distinct count in a column based on another column in sql

I have an employee table with two columns: emp_id and month_of_project. I want to find out the distinct count of employees involved in a project for a particular month. Meaning if the same person is involved in 3 months we will only count that person for the first month. I have mentioned sample below
emp_id month_of_project
101 Jan
102 Jan
103 Jan
101 Feb
104 Mar
102 Mar
105 Apr
103 Apr
The result should be
month count
Jan 3
Feb 0
Mar 1
Apr 1
Is there any way to achieve this in sql?
I think that you only should use GROUP BY clause
SELECT month_of_project, COUNT(emp_id)
FROM employees
GROUP BY month_of_project
You could use NOT EXISTS to only fetch records where no record in a previous month exists. Too bad you chose a textual representation for the month, not a numerical one. So you first have to translate it into numbers. You can use a CASE expression here.
SELECT t1.month_of_project,
count(*)
FROM elbat t1
WHERE NOT EXISTS (SELECT *
FROM elbat t2
WHERE CASE t2.month_of_project
WHEN 'Jan' THEN
1
...
WHEN 'Dec' THEN
12
END
<
CASE t1.month_of_project
WHEN 'Jan' THEN
1
...
WHEN 'Dec' THEN
12
END
AND t2.emp_id = t1.emp_id)
GROUP BY t1.month_of_project;
The sql is for oracle. And please don't store month like this in your real system. Use a date field. If your use case is just month then year can be arbitrary like 2000 and day can be 01 but atleaset for sorting etc. having true date always always always is the right idea.
First with is just to simulate data. Second in the with part mons is to get list of possible months since you want 0 for Feb. If you have that table outside you don't need that.
Then basically inner query finds the first month for employee as the month to use and outer query counts distinct
with emp_mon as
(
select '101' emp_id, to_date('20190101','YYYYMMDD') month_of_project from dual
union all select '102', to_date('20190101','YYYYMMDD') from dual
union all select '103', to_date('20190101','YYYYMMDD') from dual
union all select '101', to_date('20190201','YYYYMMDD') from dual
union all select '104', to_date('20190301','YYYYMMDD') from dual
union all select '102', to_date('20190301','YYYYMMDD') from dual
union all select '105', to_date('20190401','YYYYMMDD') from dual
union all select '103', to_date('20190401','YYYYMMDD') from dual
),
mons as
(
select distinct month_of_project
from emp_mon
)
select mons.month_of_project, count(distinct emp_first_mon.emp_id) cnt_emp_id
from mons
left outer join
(
select emp_id, min(month_of_project) month_of_project
from emp_mon
group by emp_id
) emp_first_mon on emp_first_mon.month_of_project = mons.month_of_project
group by mons.month_of_project
order by 1
SQL Sever COUNT DISTINCT will do exactly what you want. It's important to group on the correct column however.
WITH TEMP AS
(
SELECT 1 AS EMP, 1 AS MONTH_D
UNION ALL
SELECT 2 AS EMP, 1 AS MONTH_D
UNION ALL
SELECT 2 AS EMP, 1 AS MONTH_D
UNION ALL
SELECT 3 AS EMP, 1 AS MONTH_D
UNION ALL
SELECT 1 AS EMP, 2 AS MONTH_D
)
SELECT MONTH_D, COUNT(DISTINCT EMP) FROM TEMP
GROUP BY MONTH_D

Execute a oracle pl/sql query and return the result set based on the run time date value

I have below data
empid date amount
1 12-FEB-2017 10
1 12-FEB-2017 10
1 13-FEB-2017 10
1 14-FEB-2017 10
I need a query to return the total amount for a given id and date i.e, below result set
empid date amount
1 12-FEB-2017 20
1 13-FEB-2017 10
1 14-FEB-2017 10
but the think is, from the UI i will be getting the date as input.. if they pass the date return the result for that date .. if they dont pass the date return the result for most recent date.
below is the query that I wrote .. but it is working partially..
SELECT sum(amount),empid,date
FROM employee emp,
where
((date= :ddd) OR aum_valutn_dt = (select max(date) from emp))
AND emp.id = '1'
group by (empid,date)
Please help..
I think you could do something like this
but it is pretty bad you should try to do it some other way
it is doing extra work to get the most recent date
select amt, empid, date
from
(
select amt, empid, date, rank() over (order by date desc) date_rank
from
(SELECT sum(amount) amt,empid,date
FROM employee emp
where emp.id = '1'
and (date = :ddd or :ddd is null)
group by empid, date)
)
where date = :ddd or (:ddd is null and date_rank=1)
Here's another option; scans TEST table twice so ... mind the performance.
SQL> with test (empid, datum, amount) as
2 (select 1, date '2017-02-12', 10 from dual union all
3 select 1, date '2017-02-12', 10 from dual union all
4 select 1, date '2017-02-13', 10 from dual union all
5 select 1, date '2017-02-14', 10 from dual
6 )
7 select t.empid, t.datum, sum(t.amount) sum_amount
8 from test t
9 where t.datum = (select max(t1.datum)
10 from test t1
11 where t1.empid = t.empid
12 and (t1.datum = to_date('&&par_datum', 'dd.mm.yyyy')
13 or '&&par_datum' is null)
14 )
15 group by t.empid, t.datum;
Enter value for par_datum: 13.02.2017
EMPID DATUM SUM_AMOUNT
---------- ---------- ----------
1 13.02.2017 10
SQL> undefine par_datum
SQL> /
Enter value for par_datum:
EMPID DATUM SUM_AMOUNT
---------- ---------- ----------
1 14.02.2017 10
SQL>
SELECT sum(amount),empid,date
FROM employee emp,
where date =nvl((:ddd ,(select max(date) from emp))
AND emp.id = '1'
group by (empid,date)
My solution is following:
with t (empid, datum, amount) as
(select 1, date '2017-02-12', 10 from dual union all
select 1, date '2017-02-12', 10 from dual union all
select 1, date '2017-02-13', 10 from dual union all
select 1, date '2017-02-14', 10 from dual
)
select empid, datum, s
from (select empid, datum, sum(amount) s, max(datum) over (partition by empid) md
from t
group by empid, datum)
where datum = nvl(to_date(:p, 'yyyy-mm-dd'), md);
Calculate maximal date in the subquery and then, in outer subquery, compare the date with nvl(to_date(:p, 'yyyy-mm-dd'), md). If the paremeter is null, then the date field is compared with maximal date.