SQL Oracle code - not getting unique values - sql

I've got the below code which I'm wanting to give me a unique list of papf.person_number and then it should be giving me the creation date of the Security Clearance Report where one exists (which seems to be working).
My problem is it's returning multiple lines (between 3 and 6) per unique papf.person_number.
I know it might be asking a lot when you don't know the database, but hoping you can give me some suggestions of where I'm going wrong with my AND statements (company standard seems to be to use conditions rather than joins).
SELECT papf.person_number
, (ppnf.first_name||' '||ppnf.last_name) full_name
, paam.assignment_type
,(SELECT * FROM
(Select TO_CHAR(dor.creation_date, 'DD/MM/YYYY')
From hr_documents_of_record dor
WHERE dor_type.document_type = 'Security Clearance Report'
AND papf.person_id = dor.person_id
AND dor_type.document_type_id = dor.document_type_id
Order BY dor.creation_date DESC
)
WHERE ROWNUM=1) "Security Clearance Report"
FROM per_all_people_f papf
,per_person_names_f ppnf
,hr_documents_of_record dor
,hr_document_types_vl dor_type
,per_all_assignments_m paam
WHERE papf.person_id = ppnf.person_id
AND ppnf.person_id = dor.person_id
AND dor.person_id = paam.person_id
AND SYSDATE BETWEEN papf.effective_start_date AND papf.effective_end_date
AND SYSDATE BETWEEN ppnf.effective_start_date AND ppnf.effective_end_date
AND SYSDATE BETWEEN paam.effective_start_date AND paam.effective_end_date
AND paam.effective_latest_change = 'Y'
AND paam.primary_flag = 'Y'
AND ppnf.name_type = 'GLOBAL'
AND paam.assignment_status_type = 'ACTIVE'
AND paam.assignment_type IN('E','C','N','P')
AND dor_type.document_type_id = dor.document_type_id
Order by full_name

Related

Oracle SQL Case When Missing right parenthesis [duplicate]

This question already has answers here:
0RA-00907: SQL MISSING RIGHT PARENTHESIS
(1 answer)
SQL Missing right parenthesis on order by statement
(2 answers)
Closed 7 days ago.
I have this query where it selects different columns based on condition case when, I'm trying to make it work since long time but i dont know what is going on with damned case when, it just keep saying missing right parenthesis forever!!! plz help !!
no need to add any other code, if i remove the case when query works fine.
SELECT ...,
( case when PERSON_TYPE_ID= '300000000326669' then (select round(prvf.value,2)
FROM fusion.per_rates_f prf,
fusion.per_rate_values_f prvf,
fusion.per_assign_grade_steps_f pagsf,
fusion.per_all_assignments_m paam
WHERE prf.rate_id = prvf.rate_id
AND prf.rate_object_type = 'STEP'
AND prf.grade_ladder_id = paam.grade_ladder_pgm_id
AND TRUNC(cgpi.effective_date) BETWEEN prf.effective_start_date AND
prf.effective_end_date
AND prvf.rate_object_type = 'STEP'
AND TRUNC(cgpi.effective_date) BETWEEN prvf.effective_start_date AND
prvf.effective_end_date
AND pagsf.grade_step_id = prvf.rate_object_id
AND (cgpi.effective_date) BETWEEN pagsf.effective_start_date AND
pagsf.effective_end_date
AND pagsf.assignment_id = paam.assignment_id
AND paam.assignment_type = 'E'
AND PAAM.EFFECTIVE_LATEST_CHANGE = 'Y'
AND PAAM.Assignment_Status_Type = 'ACTIVE'
AND paam.person_id = papf.person_id
and papf.person_id = A.person_id
AND trunc(cgpi.effective_date) BETWEEN paam.effective_start_date AND
paam.effective_end_date
order by pagsf.effective_start_date desc
)
When PERSON_TYPE_ID ='300000001851545' then (select PPG.SEGMENT1
from FUSION.PER_ALL_ASSIGNMENTS_M paam,
FUSION.PER_PEOPLE_GROUPS PPG
WHERE 1 = 1
AND PAAM.PERSON_ID = papf.person_id
AND PAAM.ASSIGNMENT_TYPE = 'E'
AND PAAM.PRIMARY_ASSIGNMENT_FLAG = 'Y'
AND TRUNC(cgpi.effective_date) BETWEEN PAAM.EFFECTIVE_START_DATE AND
PAAM.EFFECTIVE_END_DATE
AND PAAM.ASSIGNMENT_STATUS_TYPE = 'ACTIVE'
and paam.people_group_id = ppg.people_group_id
and papf.person_id = A.person_id
AND PAAM.PERIOD_OF_SERVICE_ID =
(SELECT MAX(PPOS.PERIOD_OF_SERVICE_ID)
FROM FUSION.PER_PERIODS_OF_SERVICE PPOS
WHERE PPOS.PERSON_ID = PAAM.PERSON_ID
and ppos.date_start <= trunc(cgpi.effective_date)
and ppos.actual_termination_date =A.actual_termination_date
))
end
) Basic_Sal

Return all Users, even those who don't fit my criteria

I'm trying to return a full list of people, but only show data in the Document type column where the document type is 'Security Clearance Report'
I'm quite new to SQL and trying to figure this stuff out.
The aim is so management can easily see who has got this doc type, but also who hasn't.
SELECT papf.person_id
, (ppnf.first_name||' '||ppnf.last_name) e_name
, dor.document_name
, dor_type.document_type
, TO_CHAR(dor.creation_date, 'DD/MM/YYYY') dor_issue_date
FROM per_all_people_f papf
, per_person_names_f ppnf
, hr_document_types_vl dor_type
, hr_documents_of_record dor
WHERE SYSDATE BETWEEN papf.effective_start_date AND papf.effective_end_date
AND papf.person_id = ppnf.person_id
AND SYSDATE BETWEEN ppnf.effective_start_date AND ppnf.effective_end_date
AND ppnf.name_type = 'GLOBAL'
AND papf.person_id = dor.person_id (+)
AND dor_type.document_type_id = dor.document_type_id
AND dor_type.document_type = 'Security Clearance Report'
ORDER BY e_name DESC
As has been noted elsewhere here, this sort of thing is much simpler to do if you use ANSI style joins.
Even so, it is a little tricky because you want to LEFT JOIN your list of people to an INNER JOIN between HR_DOCUMENTS_OF_RECORD and HR_DOCUMENT_TYPES_VL. That is, for each person, you want the documents of record that are type "Security Clearance Report", if any exist.
Here is how you do that:
SELECT papf.person_id
, (ppnf.first_name||' '||ppnf.last_name) e_name
, dor.document_name
, dor_type.document_type
, TO_CHAR(dor.creation_date, 'DD/MM/YYYY') dor_issue_date
FROM per_all_people_f papf
INNER JOIN per_person_names_f ppnf ON ppnf.person_id = papf.person_id
AND ppnf.name_type = 'GLOBAL'
AND SYSDATE BETWEEN ppnf.effective_start_date AND NVL(ppnf.effective_end_date,SYSDATE)
LEFT JOIN ( hr_documents_of_record dor
INNER JOIN hr_document_types_vl dor_type ON dor_type.document_type_id = dor.document_type_id
AND dor_type.document_type = 'Security Clearance Report' ) ON dor.person_id = papf.person_id
WHERE SYSDATE BETWEEN papf.effective_start_date AND NVL(papf.effective_end_date,SYSDATE)
ORDER BY e_name DESC;
Note the INNER JOIN between DOR and DOR_TYPE is in parentheses and the LEFT JOIN condition is after the parentheses.
If you just naively LEFT JOIN everything and any people have documents of record besides security clearance reports, you'll get too many rows.
You probably want a LEFT JOIN
SELECT *
FROM per_all_people_f papf
JOIN hr_documents_of_record dor
ON papf.person_id = dor.person_id
LEFT JOIN hr_document_types_vl dor_type
ON dor_type.document_type_id = dor.document_type_id
AND dor_type.document_type = 'Security Clearance Report'
I'm trying to return a full list of people,
To get the full list of people, I would remove the requirement
AND dor_type.document_type = 'Security Clearance Report'
The aim is so management can easily see who has got this doc type, but also who hasn't.
In your select, you can do this:
, DECODE(dor_type.document_type, 'Security Clearance Report', dor_type.document_type ,null)
For any row where the document_type is the one you want, it will show it. For the others, it will just show null.
SELECT papf.person_id
, (ppnf.first_name||' '||ppnf.last_name) e_name
, dor.document_name
, DECODE(dor_type.document_type, 'Security Clearance Report', dor_type.document_type ,null)
, TO_CHAR(dor.creation_date, 'DD/MM/YYYY') dor_issue_date
FROM per_all_people_f papf
, per_person_names_f ppnf
, hr_document_types_vl dor_type
, hr_documents_of_record dor
WHERE SYSDATE BETWEEN papf.effective_start_date AND papf.effective_end_date
AND papf.person_id = ppnf.person_id
AND SYSDATE BETWEEN ppnf.effective_start_date AND ppnf.effective_end_date
AND ppnf.name_type = 'GLOBAL'
AND papf.person_id = dor.person_id (+)
AND dor_type.document_type_id = dor.document_type_id
ORDER BY e_name DESC

Outer join not working for an existing query

In the below query, I want all those person numbers that do not have records in pay_dir_cards_f and hence in corresponding pay_dir_card_components_f,
pay_dir_comp_details_f.
I have added the outer join below still am not getting the above required records. Instead I am getting records that are in the table pay_dir_cards_f.
How can i modify the outer join in the below query.
Also, please note yes I do know this join style is an old one and I should use the new convention instead but I have to update an existing query hence not using it.
SELECT DISTINCT papf.person_number,
PAPF.effective_start_date,
PAPF.effective_end_date ,
paam.ASSIGNMENT_NUMBER
FROM per_all_people_f papf,
per_all_assignments_m paam,
pay_pay_relationships_dn pprd,
pay_rel_groups_dn prgd,
pay_dir_cards_f pdcf,
pay_dir_card_components_f pdccf1,
pay_dir_comp_details_f pdcdf1
WHERE SYSDATE BETWEEN papf.effective_start_date AND
papf.effective_end_date
AND paam.person_id = papf.person_id
AND paam.effective_latest_change = 'Y'
AND paam.assignment_type IN ( 'E', 'C' )
AND pprd.person_id = papf.person_id
--And PPRD.LEGISLATIVE_DATA_GROUP_ID = PLDGV.LEGISLATIVE_DATA_GROUP_ID
AND pprd.payroll_stat_unit_id = paam.legal_entity_id
AND prgd.payroll_relationship_id = pprd.payroll_relationship_id
AND prgd.assignment_id = paam.assignment_id
AND prgd.group_type = 'A'
AND pdcf.payroll_relationship_id(+) =
prgd.payroll_relationship_id
AND Trunc(SYSDATE) BETWEEN paam.effective_start_date AND
paam.effective_end_date
AND Trunc(SYSDATE) BETWEEN papf.effective_start_date AND
papf.effective_end_date
AND Trunc(SYSDATE) BETWEEN pdcf.effective_start_date AND
pdcf.effective_end_date
AND pdccf1.dir_card_id(+) = pdcf.dir_card_id
AND pdcdf1.dir_card_comp_id(+) = pdccf1.dir_card_comp_id
AND Trunc(SYSDATE) BETWEEN pdcdf1.effective_start_date AND
pdcdf1.effective_end_date
AND pdcdf1.effective_start_date BETWEEN
pdccf1.effective_start_date AND pdccf1.effective_end_date
ORDER BY papf.person_number
I understand that you are looking to OUTER JOIN on pay_dir_cards_f and that it is not happening as you expect.
Old-style join and bad formatting make the query difficult to read... When scaning your query for the part that relate to the pay_dir_cards_f join conditions, one condition seems problematic:
AND Trunc(SYSDATE) BETWEEN pdcf.effective_start_date AND pdcf.effective_end_date
I suspect that Oracle is able to do an OUTER JOIN because of (at least) this condition. There are serious limitations and warnings againts the use of old joins, especially OUTER JOINs, which are, for example documented in the 10g manual.
I strongly recommend that you rewrite your query to use non-ancient joins. That would probably just make your problem disappear.
As a sign of good will, please find below a first attempt to rebuild (and reformat) the query. You would need to review it and probably fix a few hitches, but that should be a pretty good starting point:
SELECT DISTINCT
papf.person_number,
papf.effective_start_date,
papf.effective_end_date ,
paam.ASSIGNMENT_NUMBER
FROM
per_all_people_f papf
INNER JOIN per_all_assignments_m paam
ON paam.person_id = papf.person_id
AND paam.effective_latest_change = 'Y'
AND paam.assignment_type IN ( 'E', 'C' )
AND TRUNC(SYSDATE) BETWEEN paam.effective_start_date AND paam.effective_end_date
INNER JOIN pay_pay_relationships_dn pprd
ON pprd.person_id = papf.person_id
-- AND pprd.legislative_data_group_id = pldgv.legislative_data_group_id
AND pprd.payroll_stat_unit_id = paam.legal_entity_id
INNER JOIN pay_rel_groups_dn prgd
ON prgd.payroll_relationship_id = pprd.payroll_relationship_id
AND prgd.assignment_id = paam.assignment_id
AND prgd.group_type = 'A'
INNER JOIN pay_dir_cards_f pdcf
ON pdcf.payroll_relationship_id = prgd.payroll_relationship_id
AND TRUNC(SYSDATE) BETWEEN pdcf.effective_start_date AND pdcf.effective_end_date
LEFT JOIN pay_dir_card_components_f pdccf1
ON pdccf1.dir_card_id = pdcf.dir_card_id
AND pdccf1.dir_card_id = pdcf.dir_card_id
LEFT JOIN pay_dir_comp_details_f pdcdf1
ON TRUNC(SYSDATE) BETWEEN pdcdf1.effective_start_date AND pdcdf1.effective_end_date
AND pdcdf1.dir_card_comp_id = pdccf1.dir_card_comp_id
WHERE
SYSDATE BETWEEN papf.effective_start_date AND papf.effective_end_date
AND TRUNC(SYSDATE) BETWEEN papf.effective_start_date AND papf.effective_end_date
ORDER BY papf.person_number

outer join on subquery in the condition not working

I am using the below query to fetch few columns.
Now if the data in table cs is null i still want to fetch all the values. The outer join is working fine, but because I have a condition (select max(date_From) from cmp_Salary cs1 where cs1.assignment_id = cs.assignment_id), the employees with table CS not having any value is not fetching. How can i use NVL on this max(data_from) so that i can fetch value from this table when its null, and when there is value in the cs table , this condition is checked ?
select (CASE when PAAM.action_code = 'GLB_TRANSFER' then to_char(paam.effective_start_date,'YYYY-MM-DD') ELSE to_char(papf.START_DATE,'YYYY-MM-DD') END) "Hire_Date"
,to_char(PPS.DATE_START,'YYYY-MM-DD') "PPS Start Date"
,papf.person_number "Person_Number"
,to_char(paam.effective_start_date,'YYYY-MM-DD') "effective Start Date"
,paam.EFFECTIVE_SEQUENCE "Effective Sequence"
,paam.EFFECTIVE_LATEST_CHANGE "Effective Latest Change"
,paam.assignment_number "Assignment_Number"
,paam.action_code "Action_Code"
,(select haou.name
from hr_all_organization_units haou
where haou.organization_id = paam.LEGAL_ENTITY_ID) "Legal_Emp",
csbt.SALARY_BASIS_NAME "Salary_Basis",
to_char(cs.date_From,'YYYY-MM-DD') "Salary_Start_Date",
cs.salary_amount "Salary_Amount",
cs.currency_code new_curr_Code,
(Case cs.currency_code
when 'CAD' then '2019-07-28'
else '2019-08-05'
end) new_dt,
(case csbt.SALARY_BASIS_NAME
when 'CA Salary' then (cs.salary_amount*12)
when 'CA Salary No Time' then (cs.salary_amount*12)
else cs.salary_amount
end) new_amt
--cs.salary_amount new_amt
From per_all_people_f papf,
per_all_assignments_m paam,
cmp_salary cs,
CMP_SALARY_BASES_TL csbt,
PER_PERIODS_OF_SERVICE PPS
where papf.person_id = paam.person_id
and paam.assignment_id = cs.assignment_id(+)
and cs.SALARY_BASIS_ID = csbt.SALARY_BASIS_ID(+)
AND PAAM.PRIMARY_FLAG = 'Y'
AND PAAM.EFFECTIVE_LATEST_CHANGE = 'Y'
AND PAAM.assignment_type in ('C','E')
and sysdate BETWEEN PAAM.EFFECTIVE_START_DATE AND PAAM.EFFECTIVE_END_DATE
and sysdate BETWEEN PAPF.EFFECTIVE_START_DATE AND PAPF.EFFECTIVE_END_DATE
and cs.date_from = nvl((select max(date_From) from cmp_Salary cs1 where cs1.assignment_id = cs.assignment_id),cs.date_from )
and csbt.language = 'US'
and paam.ASSIGNMENT_STATUS_TYPE='ACTIVE'
and PPS.PERSON_ID=papf.PERSON_ID
and PPS.PERIOD_OF_SERVICE_ID=paam.PERIOD_OF_SERVICE_ID
and papf.person_number in '1755'
order by 1
You need to move this inner query in from clause.
Also, use ANSI style joins. (Recommended)
From per_all_people_f papf,
per_all_assignments_m paam,
cmp_salary cs,
CMP_SALARY_BASES_TL csbt,
PER_PERIODS_OF_SERVICE PPS,
-- added below inner view
(select max(date_From) as date_from, assignment_id
from cmp_Salary
group by assignment_id) cs1
where papf.person_id = paam.person_id
and paam.assignment_id = cs.assignment_id(+)
and cs.SALARY_BASIS_ID = csbt.SALARY_BASIS_ID(+)
AND PAAM.PRIMARY_FLAG = 'Y'
AND PAAM.EFFECTIVE_LATEST_CHANGE = 'Y'
AND PAAM.assignment_type in ('C','E')
and sysdate BETWEEN PAAM.EFFECTIVE_START_DATE AND PAAM.EFFECTIVE_END_DATE
and sysdate BETWEEN PAPF.EFFECTIVE_START_DATE AND PAPF.EFFECTIVE_END_DATE
And paam.assignment_id = cs1.assignment_id(+) -- added this
and cs.date_from = cs1.date_from(+) -- added this
and csbt.language = 'US'
and paam.ASSIGNMENT_STATUS_TYPE='ACTIVE'
and PPS.PERSON_ID=papf.PERSON_ID
and PPS.PERIOD_OF_SERVICE_ID=paam.PERIOD_OF_SERVICE_ID
and papf.person_number in '1755'
order by 1
-- Update --
Converted the joins into ANSI standard and changed the way of accessing the data from the table: CMP_SALARY.
Please check with the following query:
FROM
PER_ALL_PEOPLE_F PAPF
JOIN PER_ALL_ASSIGNMENTS_M PAAM ON ( PAPF.PERSON_ID = PAAM.PERSON_ID )
JOIN PER_PERIODS_OF_SERVICE PPS ON ( PPS.PERSON_ID = PAPF.PERSON_ID
AND PPS.PERIOD_OF_SERVICE_ID = PAAM.PERIOD_OF_SERVICE_ID )
LEFT JOIN (
SELECT
ASSIGNMENT_ID,
DATE_FROM,
SALARY_AMOUNT,
CURRENCY_CODE,
SALARY_BASIS_ID,
ROW_NUMBER() OVER(
PARTITION BY ASSIGNMENT_ID
ORDER BY
DATE_FROM DESC NULLS LAST
) AS RN
FROM
CMP_SALARY
) CS ON ( PAAM.ASSIGNMENT_ID = CS.ASSIGNMENT_ID
AND CS.RN = 1 )
LEFT JOIN CMP_SALARY_BASES_TL CSBT ON ( CS.SALARY_BASIS_ID = CSBT.SALARY_BASIS_ID )
WHERE
PAAM.PRIMARY_FLAG = 'Y'
AND PAAM.EFFECTIVE_LATEST_CHANGE = 'Y'
AND PAAM.ASSIGNMENT_TYPE IN (
'C',
'E'
)
AND SYSDATE BETWEEN PAAM.EFFECTIVE_START_DATE AND PAAM.EFFECTIVE_END_DATE
AND SYSDATE BETWEEN PAPF.EFFECTIVE_START_DATE AND PAPF.EFFECTIVE_END_DATE
AND CSBT.LANGUAGE = 'US'
AND PAAM.ASSIGNMENT_STATUS_TYPE = 'ACTIVE'
AND PAPF.PERSON_NUMBER IN '1755'
ORDER BY
1
Cheers!!

Use of count in Oracle SQL Query

I have created a query which gives two column. one is of the organization id and one is of person id. Now I want that those organizations should be retrieved which returns more than one person id.
How can I modify my below query by using count tht this can be retrieved
Query :-
select papf.person_id person_id,
hoi.organization_id
From hr_organization_information hoi
,per_all_people_f papf
where hoi.org_information2 = papf.person_id
And hoi.organization_id = 400
and sysdate between papf.effective_start_date and papf.effective_end_date
group by person_id,organization_id
Output
Person_id organization_id
123 400
678 400
So now how can I apply count in such a manner that having count(*) >1 can be used in the above query? That this organization_id will be fetched
select count(PERSON_ID)
FROM (
select papf.person_id person_id,
hoi.organization_id
From hr_organization_information hoi
,per_all_people_f papf
where hoi.org_information2 = papf.person_id
And hoi.organization_id = 400
and sysdate between papf.effective_start_date and papf.effective_end_date
group by person_id,organization_id
-- having count(*)>1
)
I Tried the above given query. But am not being able to use having in it. Also this is just returning the count. I need to retrieve the organization_id as well.
select unique hoi.organization_id
from hr_organization_information hoi,
per_all_people_f papf
where hoi.org_information2 = papf.person_id
and hoi.organization_id = 400
and sysdate between papf.effective_start_date and papf.effective_end_date
having count(papf.person_id)>1;
This will return one row with only the organization_id for the organization with an id of 400 having more than one person.
This will give you all organization_id of organizations having more than one different person_id joined:
select hoi.organization_id,
count(distinct papf.person_id) pers_count
from hr_organization_information hoi, per_all_people_f papf
where hoi.org_information2 = papf.person_id
and sysdate between papf.effective_start_date and
papf.effective_end_date
group by organization_id
having count(distinct papf.person_id) > 1
Just a note: shouldn't you add to your query information_type condition as well? As far as I remember columns content depends on defined type of information stored.
select hoi.organization_id,count(papf.person_id)
From hr_organization_information hoi inner join per_all_people_f papf
on hoi.org_information2 = papf.person_id
where hoi.organization_id = 400
and sysdate between papf.effective_start_date and papf.effective_end_date
group by hoi.organization_id having count(papf.person_id)>1;