Oracle SQL - Generate rows based on quantity column - sql

We use pooled positions that have a max headcount assigned and I need to build a report that creates a line for each head, including the details of the incumbent if there is one or a NULL line where there is a vacancy.
Like this:
Position_Title
Headcount
Incumbent
Analyst
3
Employee1
Analyst
3
Employee2
Analyst
3
I can join the Person/Assignment tables with the Position table to generate a separate line where there is an incumbent but the part i'm struggling with, is generating a line where there is a vacancy.
I spotted another post on here that suggested using connect by but I can't get it to work.
It seems to work on its own like this:
SELECT
HAP.NAME POSITION_TITLE,
HAP.POSITION_CODE,
HAP.ACTIVE_STATUS,
HAP.POSITION_TYPE,
HAP.FTE,
LEVEL row_num
FROM
HR_ALL_POSITIONS_F_VL HAP
CONNECT BY LEVEL <= HAP.FTE
AND PRIOR HAP.POSITION_ID = HAP.POSITION_ID
AND PRIOR sys_guid() IS NOT NULL
But I'm not sure how to use it with the rest of my query (I've tried using the WHERE clause before and after the CONNECT BY but it times out either way)
SELECT
HAP.NAME POSITION_TITLE,
HAP.POSITION_CODE,
PGF.NAME GRADE_NAME,
PGF.GRADE_CODE,
HAP.ACTIVE_STATUS,
HAP.POSITION_TYPE,
HAP.HEADCOUNT,
PAAM.ASSIGNMENT_NUMBER,
LEVEL row_num
FROM
HR_ALL_POSITIONS_F_VL HAP,
PER_GRADES_F_VL PGF,
PER_ALL_ASSIGNMENTS_M PAAM
WHERE
HAP.ENTRY_GRADE_ID = PGF.GRADE_ID
AND PAAM.POSITION_ID(+) = HAP.POSITION_ID
AND TRUNC(Sysdate) between HAP.effective_start_date AND HAP.effective_end_date
AND TRUNC(Sysdate) between PGF.effective_start_date AND PGF.effective_end_date
AND PAAM.effective_start_date(+) <= TRUNC(Sysdate)
AND PAAM.effective_end_date(+) >= TRUNC(Sysdate)
CONNECT BY LEVEL <= HAP.HEADCOUNT
AND PRIOR HAP.POSITION_ID = HAP.POSITION_ID
AND PRIOR sys_guid() IS NOT NULL

You can use multiset to generate rows as per the headcount column as follows:
SELECT
HAP.NAME POSITION_TITLE,
HAP.POSITION_CODE,
PGF.NAME GRADE_NAME,
PGF.GRADE_CODE,
HAP.ACTIVE_STATUS,
HAP.POSITION_TYPE,
HAP.HEADCOUNT,
PAAM.ASSIGNMENT_NUMBER,
Lvls.Column_value row_num
FROM
HR_ALL_POSITIONS_F_VL HAP,
PER_GRADES_F_VL PGF,
PER_ALL_ASSIGNMENTS_M PAAM,
table(cast(multiset(select level from dual connect by level <= hap.headcount) as sys.OdciNumberList)) lvls
WHERE
HAP.ENTRY_GRADE_ID = PGF.GRADE_ID
AND PAAM.POSITION_ID(+) = HAP.POSITION_ID
AND TRUNC(Sysdate) between HAP.effective_start_date AND HAP.effective_end_date
AND TRUNC(Sysdate) between PGF.effective_start_date AND PGF.effective_end_date
AND PAAM.effective_start_date(+) <= TRUNC(Sysdate)
AND PAAM.effective_end_date(+) >= TRUNC(Sysdate);
Note: add the condition as per your requirement and always use standard ANSI joins.

Related

How to get max of one date in a table in sql [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
GROUP BY with MAX(DATE) [duplicate]
(6 answers)
Oracle SQL query: Retrieve latest values per group based on time [duplicate]
(2 answers)
Return row with the max value of one column per group [duplicate]
(3 answers)
Get value based on max of a different column grouped by another column [duplicate]
(1 answer)
Closed 2 years ago.
I have written the below code -
select
PAPF.PERSON_NUMBER,
BP.NAME BENEFIT_PLAN,
BBR.BENEFIT_RELATION_NAME,
Round(BPER.BNFT_AMT, 2) * 100 COVERAGE_AMOUNT,
TO_CHAR(BPER.ENRT_CVG_STRT_DT, 'YYYYMMDD') ENROLMENTCOVSTARTDATE
TO_CHAR(BPER.ENRT_CVG_THRU_DT, 'YYYYMMDD') ENROLMENTCOVSENDDATE
FROM PER_ALL_PEOPLE_F PAPF,
BEN_PRTT_ENRT_RSLT BPER,
BEN_PL_F BP,
BEN_BENEFIT_RELATIONS_F BBR
WHERE PAPF.PERSON_ID = BPER.PERSON_ID
AND BPER.PL_ID = BP.PL_ID
AND BBR.PERSON_ID = PAPF.PERSON_ID
AND BBR.BENEFIT_RELATION_NAME = 'DFLT'
AND SYSDATE BETWEEN PAPF.EFFECTIVE_START_DATE AND PAPF.EFFECTIVE_END_DATE
AND SYSDATE BETWEEN BP.EFFECTIVE_START_DATE AND BP.EFFECTIVE_END_DATE
AND SYSDATE BETWEEN BBR.EFFECTIVE_START_DATE AND BBR.EFFECTIVE_END_DATE
This will give me an output like -
PErson_number BENEFIT_PLAN COVERAGE_AMOUNT ENROLMENTCOVSTARTDATE ENROLMENTCOVSENDDATE
1010 US Basic PLAN 20000 20200901 20201020
1010 US Basic PLAN 20000 20201021
1011 Us Spouse PLAN 160000 20200901 20201020
1011 Us Spouse PLAN 160000 20201021 47121231
I want to retrive only the max of the ENROLMENTCOVSTARTDATE for each person. The expected output should be -
PErson_number BENEFIT_PLAN COVERAGE_AMOUNT ENROLMENTCOVSTARTDATE ENROLMENTCOVSENDDATE
1010 US Basic PLAN 20000 20201021
1011 Us Spouse PLAN 160000 20201021 20201220
how can i use Max in the main query for this ?
First rank the rows by their ENRT_CVG_STRT_DT per person (assumed identified by PAPF.PERSON_NUMBER). Then filter on rows that are top.
select person_number,
name,
BENEFIT_PLAN,
BENEFIT_RELATION_NAME,
COVERAGE_AMOUNT,
ENROLMENTCOVSTARTDATE,
ENROLMENTCOVSENDDATE
from (
select
PAPF.PERSON_NUMBER,
BP.NAME BENEFIT_PLAN,
BBR.BENEFIT_RELATION_NAME,
Round(BPER.BNFT_AMT, 2) * 100 COVERAGE_AMOUNT,
TO_CHAR(BPER.ENRT_CVG_STRT_DT, 'YYYYMMDD') ENROLMENTCOVSTARTDATE,
TO_CHAR(BPER.ENRT_CVG_THRU_DT, 'YYYYMMDD') ENROLMENTCOVSENDDATE,
row_number() over (partition by PAPF.PERSON_NUMBER order by BPER.ENRT_CVG_STRT_DT desc) rn
from
FROM PER_ALL_PEOPLE_F PAPF,
BEN_PRTT_ENRT_RSLT BPER,
BEN_PL_F BP,
BEN_BENEFIT_RELATIONS_F BBR
WHERE PAPF.PERSON_ID = BPER.PERSON_ID
AND BPER.PL_ID = BP.PL_ID
AND BBR.PERSON_ID = PAPF.PERSON_ID
AND BBR.BENEFIT_RELATION_NAME = 'DFLT'
AND SYSDATE BETWEEN PAPF.EFFECTIVE_START_DATE AND PAPF.EFFECTIVE_END_DATE
AND SYSDATE BETWEEN BP.EFFECTIVE_START_DATE AND BP.EFFECTIVE_END_DATE
AND SYSDATE BETWEEN BBR.EFFECTIVE_START_DATE AND BBR.EFFECTIVE_END_DATE
)
where rn = 1
If you want to accept tied first place then you would change row_number to rank/dense_rank (it won't matter if you're filtering on rn = 1). One would assume that there would only be one row per person per ENROLMENTCOVSTARTDATE though.

How to limit return results

My query returns duplicate rows and I want to return only one row.
select papf.Full_Name,
papf.Employee_number,
papf.DatE_OF_BIRTH,
peef.last_update_date
/*,
petf1.ELEMENT_NAME as MedicalSchemeName*/
from per_all_people_f papf
,per_all_assignments_f paaf
,PAY_ELEMENT_ENTRIES_F peef
,pay_element_types_f petf
--,fnd_user fnu
where (papf.Employee_number) not in
(select
papf1.Employee_number
from per_all_people_f papfinner
,per_all_assignments_f paafinner
,PAY_ELEMENT_ENTRIES_F peefinner
,pay_element_types_f petfinner
where paafinner.person_id = papfinner.person_id
and peefinner.assignment_id = paafinner.assignment_id
and peefinner.element_type_id = petfinner.element_type_id
and upper(petf1.ELEMENT_NAME) like '%Condition%'
and SYSDATE BETWEEN papfinner.EFFECTIVE_START_DATE AND papfinner.EFFECTIVE_END_DATE
and SYSDATE BETWEEN paafinner.EFFECTIVE_START_DATE AND paafinner.EFFECTIVE_END_DATE
and SYSDATE BETWEEN peefinner.EFFECTIVE_START_DATE AND peefinner.EFFECTIVE_END_DATE
and SYSDATE BETWEEN petfinner.EFFECTIVE_START_DATE AND petfinner.EFFECTIVE_END_DATE
)
and SYSDATE BETWEEN papf.EFFECTIVE_START_DATE AND papf.EFFECTIVE_END_DATE
and SYSDATE BETWEEN paaf.EFFECTIVE_START_DATE AND paaf.EFFECTIVE_END_DATE
and SYSDATE BETWEEN peef.EFFECTIVE_START_DATE AND peef.EFFECTIVE_END_DATE
and SYSDATE BETWEEN petf.EFFECTIVE_START_DATE AND petf.EFFECTIVE_END_DATE
and upper(petf.ELEMENT_NAME) not like '%Condition%'
and rownum <= 10000
order by peef.last_update_date;
....
This query works properly and returns the correct results but the results are duplicated. I only need one unique row
One of the tables you are joining one is returning doubles or more. I bet it is "peef". Perhaps you can only show the MAX(last_update_date) and roll the rest up against that. So just GROUP BY.
select papf.Full_Name,
papf.Employee_number,
papf.DatE_OF_BIRTH,
last_update_date = MAX(peef.last_update_date)
...
GROUP BY
papf.Full_Name,
papf.Employee_number,
papf.DatE_OF_BIRTH

List all months with a total regardless of null

I have a very small SQL table that lists courses attended and the date of attendance. I can use the code below to count the attendees for each month
select to_char(DATE_ATTENDED,'YYYY/MM'),
COUNT (*)
FROM TRAINING_COURSE_ATTENDED
WHERE COURSE_ATTENDED = 'Fire Safety'
GROUP BY to_char(DATE_ATTENDED,'YYYY/MM')
ORDER BY to_char(DATE_ATTENDED,'YYYY/MM')
This returns a list as expected for each month that has attendees. However I would like to list it as
January 2
February 0
March 5
How do I show the count results along with the nulls? My table is very basic
1234 01-JAN-15 Fire Safety
108 01-JAN-15 Fire Safety
1443 02-DEC-15 Healthcare
1388 03-FEB-15 Emergency
1355 06-MAR-15 Fire Safety
1322 09-SEP-15 Fire Safety
1234 11-DEC-15 Fire Safety
I just need to display each month and the total attendees for Fire Safety only. Not used SQL developer for a while so any help appreciated.
You would need a calendar table to select a period you want to display. Simplified code would look like this:
select to_char(c.Date_dt,'YYYY/MM')
, COUNT (*)
FROM calendar as c
left join TRAINING_COURSE_ATTENDED as tca
on tca.DATE_ATTENDED = c.Date_dt
WHERE tca.COURSE_ATTENDED = 'Fire Safety'
and c.Date_dt between [period_start_dt] and [period_end_dt]
GROUP BY to_char(c.Date_dt,'YYYY/MM')
ORDER BY to_char(c.Date_dt,'YYYY/MM')
You can create your own set required year month's on-fly with 0 count and use query as below.
Select yrmth,sum(counter) from
(
select to_char(date_attended,'YYYYMM') yrmth,
COUNT (1) counter
From TRAINING_COURSE_ATTENDED Where COURSE_ATTENDED = 'Fire Safety'
Group By Y to_char(date_attended,'YYYYMM')
Union All
Select To_Char(2015||Lpad(Rownum,2,0)),0 from Dual Connect By Rownum <= 12
)
group by yrmth
order by 1
If you want to show multiple year's, just change the 2nd query to
Select To_Char(Year||Lpad(Month,2,0)) , 0
From
(select Rownum Month from Dual Connect By Rownum <= 12),
(select 2015+Rownum-1 Year from Dual Connect By Rownum <= 3)
Try this :
SELECT Trunc(date_attended, 'MM') Month,
Sum(CASE
WHEN course_attended = 'Fire Safety' THEN 1
ELSE 0
END) Fire_Safety
FROM training_course_attended
GROUP BY Trunc(date_attended, 'MM')
ORDER BY Trunc(date_attended, 'MM')
Another way to generate a calendar table inline:
with calendar (month_start, month_end) as
( select add_months(date '2014-12-01', rownum)
, add_months(date '2014-12-01', rownum +1) - interval '1' second
from dual
connect by rownum <= 12 )
select to_char(c.month_start,'YYYY/MM') as course_month
, count(tca.course_attended) as attended
from calendar c
left join training_course_attended tca
on tca.date_attended between c.month_start and c.month_end
and tca.course_attended = 'Fire Safety'
group by to_char(c.month_start,'YYYY/MM')
order by 1;
(You could also have only the month start in the calendar table, and join on trunc(tca.date_attended,'MONTH') = c.month_start, though if you had indexes or partitioning on tca.date_attended that might be less efficient.)

To list 12 months in a query Oracle SQL

I need your help, please.
I have a query to list a PDAs numbers and I need to sort this in twelve months. My query:
SELECT TO_CHAR(primeirodia_mes,'mm/yyyy') competencia,
'Suporte' tipo,
(SELECT COUNT(tipo)
FROM v_pdas_suporte_se
WHERE TO_CHAR(primeirodia_mes,'mm/yyyy') = TO_CHAR(v_pdas_suporte_se.dt,'mm/yyyy')
AND tipo IN ( 'S','E')
) quantidade
FROM
(SELECT To_Date( '01/'
||LPad(ID,2,0)
||'/'
||TO_CHAR(SYSDATE,'yyyy') ,'dd/mm/yyyy') primeirodia_mes
FROM
(SELECT LEVEL AS ID FROM DUAL CONNECT BY LEVEL <= 12 )
) contador
I need to list , for example, Oct/2014 to Oct/2015.
I assume you are looking for this:
WITH t AS
(SELECT ADD_MONTHS(TRUNC(SYSDATE, 'MM'), - LEVEL+1) AS primeirodia_mes
FROM dual CONNECT BY LEVEL <= 13)
SELECT
primeirodia_mes AS competencia,
'Suporte' tipo,
COUNT(tipo)
FROM v_pdas_suporte_se
RIGHT OUTER JOIN t ON primeirodia_mes = TRUNC(dt, 'MM')
GROUP BY primeirodia_mes;

How to check newer than or equal to a date on access SQL

Currently I have two tables, using Access 2007
TimeSheet(empID, TimeSheet, hours)
and
Rates(empID,Rate,PromotionDate)
How do I select the correct billing rates of employee based on their date of promotion?
For example, I have
Rates(001,10,#01/01/2013#)
Rates(001,15,#01/05/2013#)
Rates(002,10,#01/01/2013#)
and
Timesheet(001,#02/01/2013#,5)
Timesheet(001,#02/05/2013#,5)
Timesheet(002,#02/01/2013#,7)
In this case, I want to show that if empID 001 submited a time sheet at 02/01/2013 it would be billed with $10/hr
, but his timesheets starting at May 1st would be billed with $15/hr
My query right now is
SELECT t.empID , t.timesheet, r.hours ,
(SELECT rate FROM rate WHERE t.timeSheet >= r.promotionDate) AS RateBilled
FROM rate AS r , timesheet AS t
WHERE r.empid = t.empid
When ran, it shows a message of “At most one record can be returned by this subquery”
Any help would be appreciated, thanks.
Edit:
I have some strange output using the sql
SELECT t.empID, t.timesheet, r.rate AS RateBilled
FROM Rates AS r, timesheet AS t
WHERE r.empid=t.empid And t.timeSheet>=r.promotionDate
GROUP BY t.empID, t.timesheet, r.rate, r.promotionDate
HAVING r.promotionDate=MAX(r.promotionDate);
as you can see the output table ratebilled for empID 1 is switching back and forth from 10 to 15, even though past May 01, it should all be using 15 ,
any help is appreciated, thanks.
The select subquery you have setup potentially returns multiple values where only one should be returned. Consider the case where there may be two promotions and a recent timesheet, then the select will return two values because on both occasions the timesheet is newer than the promotion.
Try using the following as your subquery:
SELECT TOP 1 rate FROM rate
WHERE t.timeSheet >= r.promotionDate
ORDER BY r.promotionDate DESC
N.B. I don't think the above is terribly efficient. Instead try something like
SELECT t.empID , t.timesheet, r.hours , r.rate AS RateBilled
FROM rate AS r , timesheet AS t
WHERE r.empid = t.empid AND t.timeSheet >= r.promotionDate
GROUP BY t.empID, t.timesheet
HAVING r.promotionDate = MAX( r.promotionDate);