Select Max date column in a sql query, which is joined to other columns - sql

I have an Oracle Query below
select papf.person_number, to_char(hrcd.SUBMITTED_DATE, 'DD-Mon-YYYY','NLS_DATE_LANGUAGE = American') as "Resignation Date", paam.last_update_date as assignmentupdate, hrcd.last_update_date as hrcdupdate, hth.last_update_date as hthupdate
from
per_all_people_f papf, per_all_assignments_m paam, hrc_txn_header hth, HRC_TXN_DATA hrcd
where
papf.person_id=paam.person_id
and paam.assignment_type IN ('E','C')
and paam.assignment_status_type in ('ACTIVE')
and paam.primary_flag = 'Y'
and paam.EFFECTIVE_LATEST_CHANGE ='Y'
and trunc(sysdate) between trunc(paam.effective_start_date) and trunc(paam.effective_end_date)
and trunc(sysdate) between trunc(papf.effective_start_date) and trunc(papf.effective_end_date)
and paam.person_id=hth.subject_id(+)
and paam.assignment_id=hth.object_id(+)
and hth.module_identifier(+) IN ('Resignation','Terminations')
and hrcd.transaction_id(+)=hth.transaction_id
and papf.person_number IN ('901626', '900723', '900846');
Which is giving below result
Now I am Person 900846 has multiple rows coming, now I need to extract the max date from this column for that I added the below logic but this is not working
select papf.person_number, to_char(hrcd.SUBMITTED_DATE, 'DD-Mon-YYYY','NLS_DATE_LANGUAGE = American') as "Resignation Date", paam.last_update_date as assignmentupdate, hrcd.last_update_date as hrcdupdate, hth.last_update_date as hthupdate
from
per_all_people_f papf, per_all_assignments_m paam, hrc_txn_header hth, HRC_TXN_DATA hrcd
where
papf.person_id=paam.person_id
and paam.assignment_type IN ('E','C')
and paam.assignment_status_type in ('ACTIVE')
and paam.primary_flag = 'Y'
and paam.EFFECTIVE_LATEST_CHANGE ='Y'
and trunc(sysdate) between trunc(paam.effective_start_date) and trunc(paam.effective_end_date)
and trunc(sysdate) between trunc(papf.effective_start_date) and trunc(papf.effective_end_date)
and paam.person_id=hth.subject_id(+)
and paam.assignment_id=hth.object_id(+)
and hth.module_identifier(+) IN ('Resignation','Terminations')
and hrcd.transaction_id(+)=hth.transaction_id
and hrcd.SUBMITTED_DATE =
(SELECT MAX(hrcd2.SUBMITTED_DATE)
FROM HRC_TXN_DATA hrcd2, hrc_txn_header hth2, per_all_assignments_m paam2
WHERE hrcd2.TRANSACTION_ID = hrcd.TRANSACTION_ID
AND hrcd2.transaction_id=hth2.transaction_id
and hth2.module_identifier IN ('Resignation','Terminations')
and paam2.assignment_id=hth2.object_id
and paam2.assignment_id=paam.assignment_id
)
and papf.person_number IN ('901626', '900723', '900846');
But now the Output is like below
I am not sure how the outer join will be added in the second query and even after after that the result should 3 lines of data each for employee with max resignation date and blank is resignation date is null
Output should look like below
can someone help me in this?
Thanks,
Shivam

You can use window functions (ROW_NUMBER | RANK | DENSE_RANK | MAX OVER) to rank your rows and only pick the latest per person. E.g.:
select person_number, "Resignation Date", assignmentupdate, hrcdupdate, hthupdate
from
(
select
papf.person_number,
to_char(hrcd.submitted_date, 'DD-Mon-YYYY', 'NLS_DATE_LANGUAGE = American') as "Resignation Date",
paam.last_update_date as assignmentupdate,
hrcd.last_update_date as hrcdupdate,
hth.last_update_date as hthupdate,
hrcd.submitted_date,
max(hrcd.submitted_date) over (partition by papf.person_number) as max_submitted_date
from per_all_people_f papf
join per_all_assignments_m paam on paam.person_id = papf.person_id
left join hrc_txn_header hth on hth.subject_id = paam.person_id
and hth.object_id = paam.assignment_id
and hth.module_identifier IN ('Resignation', 'Terminations')
left join HRC_TXN_DATA hrcd on hrcd.transaction_id = hth.transaction_id
where papf.person_number IN (901626, 900723, 900846)
and paam.assignment_type IN ('E', 'C')
and paam.assignment_status_type in ('ACTIVE')
and paam.primary_flag = 'Y'
and paam.effective_latest_change = 'Y'
and trunc(sysdate) between trunc(paam.effective_start_date) and trunc(paam.effective_end_date)
and trunc(sysdate) between trunc(papf.effective_start_date) and trunc(papf.effective_end_date)
)
where (submitted_date = max_submitted_date)
or (submitted_date is null and max_submitted_date is null)
order by person_number;

Related

Sql returning with 4-5 minutes delay [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 21 days ago.
Improve this question
I'm trying to fetch the details of employee payment from Oracle Fusion HCM tables with he help of below SQL query but the results I'm getting with 4-5 minutes of delay. Need help on how can I improve the performance of this SQL query.
As I'm new to SQL and not aware of all the functionalities and best practices available in SQL. Please help as this query is from the support project what I'm working in
SELECT DISTINCT papf.person_number,
ppnf.full_name name,
bal.balance_value,
fi.instance_name,
fabu.bu_name
FROM pay_payroll_actions ppa,
pay_all_payrolls_f pay,
pay_payroll_rel_actions pra,
pay_pay_relationships_dn pprd,
per_all_people_f papf,
per_all_assignments_m paam,
per_person_names_f ppnf,
fun_all_business_units_v fabu,
pay_time_periods ptp,
pay_person_pay_methods_f ppm,
pay_action_classes pac,
pay_balance_types_vl pbt,
PAY_FLOW_INSTANCES fi,
PAY_REQUESTS pr,
TABLE(
pay_balance_view_pkg.get_balance_dimensions (
p_balance_type_id => pbt.balance_type_id,
p_payroll_rel_action_id => pra.payroll_rel_action_id,
p_payroll_term_id => NULL,
p_payroll_assignment_id => NULL
)
) bal,
pay_dimension_usages_vl pdu,
PAY_ORG_PAY_METHODS_F popf1
WHERE ppa.action_type IN ('Q', 'R')
AND ppa.effective_date BETWEEN to_date(
:p_pay_period,
'MON-YY',
'nls_date_language=American'
) AND LAST_DAY(
to_date(
:p_pay_period,
'MON-YY',
'nls_date_language=American'
)
)
AND pay.payroll_id = ppa.payroll_id
AND ppa.PAYROLL_ACTION_ID = pra.PAYROLL_ACTION_ID
AND pr.PAY_REQUEST_ID = ppa.PAY_REQUEST_ID
AND fi.FLOW_INSTANCE_ID = pr.FLOW_INSTANCE_ID
AND pay.payroll_name = NVL(:p_payroll, pay.payroll_name)
AND ppa.effective_date BETWEEN pay.effective_start_date AND pay.effective_end_date
AND pra.payroll_action_id = ppa.payroll_action_id
AND pra.retro_component_id IS NULL
AND pra.action_status = 'C'
AND pprd.payroll_relationship_id = pra.payroll_relationship_id
AND ppa.effective_date BETWEEN pprd.start_date AND pprd.end_date
AND papf.person_id = pprd.person_id
AND ppa.effective_date BETWEEN papf.effective_start_date AND papf.effective_end_date
AND paam.person_id = papf.person_id
AND paam.assignment_type = 'E'
AND paam.primary_flag = 'Y'
AND paam.effective_latest_change = 'Y'
AND ppa.effective_date BETWEEN paam.effective_start_date AND paam.effective_end_date
AND ppnf.person_id = pprd.person_id
AND ppnf.name_type = 'GLOBAL'
AND ppa.effective_date BETWEEN ppnf.effective_start_date AND ppnf.effective_end_date
AND fabu.bu_id = paam.business_unit_id
AND ppa.effective_date BETWEEN fabu.date_from AND fabu.date_to
AND (
LEAST(:company_name) IS NULL
OR (
DECODE(
:company_name,
'Company 1',
1,
0
) = 1
AND fabu.bu_name IN (
'Company 2',
'Shared Service BU'
)
)
OR (
DECODE(
:company_name,
'Company 3',
1,
0
) = 0
AND fabu.bu_name IN (:company_name)
)
)
AND pay.payroll_id = ptp.payroll_id
AND ptp.period_category IN ('E', 'C')
AND TO_CHAR(
ptp.start_date,
'MON-YY',
'NLS_DATE_LANGUAGE = american'
) IN (:p_pay_period)
AND ppm.payroll_relationship_id(+) = pprd.payroll_relationship_id
AND pac.action_type = ppa.action_type
AND pac.classification_name = 'SEQUENCED'
AND pbt.legislation_code = 'SA'
AND pbt.balance_name IN ('Net Pay')
AND pdu.database_item_suffix = '_REL_RUN'
AND pdu.balance_dimension_id = bal.balance_dimension_id
AND pdu.legislation_code = 'SA'
AND bal.balance_value <> 0
AND ppm.org_payment_method_id = popf1.org_payment_method_id(+)
AND EXISTS (
SELECT 1
FROM pay_paymt_search_results_vl ppsrv,
pay_action_interlocks pai,
pay_payroll_rel_actions pre_rel_actions,
pay_payroll_actions pre_actions
WHERE (
ppsrv.person_number = pprd.payroll_relationship_number
OR ppsrv.person_number || '-1' = pprd.payroll_relationship_number
OR ppsrv.person_number || '-2' = pprd.payroll_relationship_number
OR ppsrv.person_number || '-3' = pprd.payroll_relationship_number
)
AND pai.locked_action_id = pra.payroll_rel_action_id
AND pre_rel_actions.payroll_rel_action_id = pai.locking_action_id
AND pre_actions.payroll_action_id = pre_rel_actions.payroll_action_id
AND ppsrv.opm IN (:PAYMENT_METHOD)
AND ppsrv.payroll_rel_action_id = pre_rel_actions.payroll_rel_action_id
AND ppsrv.process_date BETWEEN ptp.start_date AND ptp.end_date
)
ORDER BY papf.person_number
DISTINCT is never fast
Lot of WHERE conditions will not use any indexes because of the usage of functions (conversion of dates, DECODE, ...)
the DECODEs could be rewrite in more efficient way
testing for NULL doesn't use an index - if any - unless part of a compound one
the usage of the TABLE() on a (pipelined?) function could be optimized because only 2 conditions are used on the result: you should better have a more suited get_balance_dimensions() version to which you pass the conditions (pdu.balance_dimension_id and balance_value <> 0), the filtering inside the function will most probably speed up the processing (more extreme solution would be to put it in a CTE with materialize hint)
LEAST on 1 argument is useless (but the optimizer should strip it) but raises the question: is really what you want to do ?
...
I don't know if your data needs to be real time but if not, I wonder if it would be prudent to create an additional table to hold the complexity of this query and update it on a schedule (nightly?). Have the new table do all the joins and math when a 5 min query doesn't hurt anything leaving this query as a simple select * from payTablesCombined.
you should use this solution
SELECT
...........
FROM pay_payroll_actions ppa
left join pay_all_payrolls_f pay on pay.payroll_id = ppa.payroll_id
.....
Should use join

SQL query to get the data as of 31/03 of past three years

I have created the above query which give me the Bonus tagged to the person as of sysdate.
select
person_number ,
peef.effective_start_Date,
peef.value Amount
from
per_all_people_f papf,
pay_element_entries_f peef
where
papf.person_id = peef.person_id
and PEEF.element_name in ('Bonus')
and sysdate between peef.effective_start_Date and peef.effective_end_Date
I want to tweak the above query to get the Amount for past three years as of 31/3 i.e. instead of sysdate as of 31/3/2021, 31/03/2020,31/03/2019
Output like -
Person_NUMBER effective_start_Date current_Amount 2021_AMOUNT 2020_AMOUNT 2019_AMOUNT
How can i tweak the same query and change the sysdate condition to look for data for past three years as well for the 2021_amount, 2020_amount and 2019_amount column
You can move the last line to the SELECT list as conditionals such as
SELECT person_number, peef.effective_start_Date,
peef.value AS current_amount,
CASE WHEN date'2021-03-31' BETWEEN peef.effective_start_Date AND peef.effective_end_Date
THEN peef.value
END AS 2021_amount,
CASE WHEN date'2020-03-31' BETWEEN peef.effective_start_Date AND peef.effective_end_Date
THEN peef.value
END AS 2020_amount,
CASE WHEN date'2019-03-31' BETWEEN peef.effective_start_Date AND peef.effective_end_Date
THEN peef.value
END AS 2019_amount
FROM per_all_people_f papf
JOIN pay_element_entries_f peef
ON peef.person_id = papf.person_id
WHERE peef.element_name = 'Bonus'
in order to get the currently displayed result, but in this case there will be multiple lines with NULL values for amount columns. I suspect that you need to get aggregated results in order to show the summed up amounts, then consider adding SUM() aggregation such as
SELECT person_number, peef.effective_start_Date,
SUM( peef.value ) AS current_amount,
SUM( CASE WHEN date'2021-03-31' BETWEEN peef.effective_start_Date AND peef.effective_end_Date
THEN peef.value
END ) AS 2021_amount,
SUM( CASE WHEN date'2020-03-31' BETWEEN peef.effective_start_Date AND peef.effective_end_Date
THEN peef.value
END ) AS 2020_amount,
SUM( CASE WHEN date'2019-03-31' BETWEEN peef.effective_start_Date AND peef.effective_end_Date
THEN peef.value
END ) AS 2019_amount
FROM per_all_people_f papf
JOIN pay_element_entries_f peef
ON peef.person_id = papf.person_id
WHERE peef.element_name = 'Bonus'
GROUP BY person_number, peef.effective_start_Date

SQL employee Query to get annual salary Oracle HRMS

Is there a way to get an employee's annual salary using their FTE in a SQL query?
This is the query to get the salary. How do I incorporate the FTE component?
SELECT papf.employee_number,papf.full_name,pj.NAME job,
pap.NAME as Designation ,pg.NAME as Grade , haou.NAME
ORGANIZATION,ppp.creation_date createDate,
ppp.PROPOSED_SALARY as salary
FROM per_all_people_f papf
,per_all_assignments_f paaf
,per_jobs pj
,per_grades pg
,hr_all_organization_units haou
,per_position_definitions ppd
,per_all_positions pap
,per_pay_proposals ppp
WHERE SYSDATE BETWEEN papf.effective_start_date AND papf.effective_end_date
AND papf.current_employee_flag = 'Y'
AND papf.employee_number IS NOT NULL
AND paaf.person_id = papf.person_id
AND SYSDATE BETWEEN paaf.effective_start_date AND paaf.effective_end_date
AND paaf.job_id = pj.job_id
AND paaf.organization_id = haou.organization_id
AND paaf.grade_id = pg.grade_id
AND paaf.position_id = pap.position_id
AND pap.position_definition_id = ppd.position_definition_id
AND ppp.pay_proposal_id in (SELECT MAX (pay_proposal_id)
FROM per_pay_proposals
WHERE assignment_id = paaf.assignment_id)
order by to_number(papf.employee_number)

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

Oracle sql window functions - total on same row as results

I would like to show the total (labour turnover) and results on the same line using a window function, but I can't get it to work. It gives me "from keyword not found where expected" on the over clause in the total_labour_turnover column.
Basically I would like the total labour turnover to be displayed along with the individual rows' labour turnover. Note that I do not want a running total.
Anyone know how to do this?
select active.cost_centre,
active.flexi_perm,
active.active_count,
nvl(term.term_count, 0) term_count,
round(nvl(term.term_count, 0) / active.active_count * 100, 2) labour_turnover,
round(sum(nvl(term.term_count, 0)) / sum(active.active_count) * 100, 2) over (order by 1) total_labour_turnover
from (
select haou.name cost_centre,
decode(payr.attribute2, 'F', 'Flexi', 'Perm') flexi_perm,
count(paaf.assignment_id) active_count
from per_periods_of_service ppos,
per_all_assignments_f paaf,
hr_organization_information hoi,
hr_all_organization_units haou,
pay_all_payrolls_f payr
where trunc(:active_count_date) between ppos.date_start and nvl(ppos.actual_termination_date, to_date('31/12/4712', 'dd/mm/yyyy'))
and paaf.period_of_service_id = ppos.period_of_service_id
and paaf.primary_flag = 'Y'
and trunc(:active_count_date) between paaf.effective_start_date and paaf.effective_end_date
and hoi.org_information_context = 'TRU_ADD_ORG'
and hoi.organization_id = paaf.organization_id
and haou.organization_id = paaf.organization_id
and payr.payroll_id = paaf.payroll_id
and payr.attribute2 in ('F', 'N') -- Flexi and Non-Flexi
and trunc(:active_count_date) between payr.effective_start_date and payr.effective_end_date
group by haou.name,
decode(payr.attribute2, 'F', 'Flexi', 'Perm')
) active,
(
select haou.name cost_centre,
decode(payr.attribute2, 'F', 'Flexi', 'Perm') flexi_perm,
count(distinct paaf.person_id) term_count
from per_periods_of_service ppos,
per_all_assignments_f paaf,
hr_organization_information hoi,
hr_all_organization_units haou,
pay_all_payrolls_f payr
where nvl(ppos.actual_termination_date, to_date('31/12/4712', 'dd/mm/yyyy')) between trunc(:term_start) and trunc(:term_end)
and paaf.period_of_service_id = ppos.period_of_service_id
and paaf.primary_flag = 'Y'
and nvl(ppos.actual_termination_date, to_date('31/12/4712', 'dd/mm/yyyy')) between paaf.effective_start_date and paaf.effective_end_date
and hoi.org_information_context = 'TRU_ADD_ORG'
and hoi.organization_id = paaf.organization_id
and haou.organization_id = paaf.organization_id
and payr.payroll_id = paaf.payroll_id
and payr.attribute2 in ('F', 'N') -- Flexi and Non-Flexi
and nvl(ppos.actual_termination_date, to_date('31/12/4712', 'dd/mm/yyyy')) between payr.effective_start_date and payr.effective_end_date
group by haou.name,
decode(payr.attribute2, 'F', 'Flexi', 'Perm')
) term
where term.cost_centre (+) = active.cost_centre
and term.flexi_perm (+) = active.flexi_perm
Please try the following, it should help:
round(sum(nvl(term.term_count, 0)) over() / sum(active.active_count) over() * 100, 2) total_labour_turnover
You have two SUM functions in your expression a both of them need be a analytic function.
According to Oracle documentation on analytic functions, the order by clause automatically implies a running window of rows between unbounded preceding and current row.
If you don't want a running sum, but a total sum, delete the order by 1 from the over clause of your analytical sum() function and put a partition by null there instead. Or try it with empty parentheses, might work, too.