Oracle 'CONNECT BY' Syntax - sql

This is an offshoot of the following question:
Single out duplicates between two result sets
As by a comment in that questions, I'm trying to implement my query using Oracle's special 'CONNECT BY' syntax. I'm having trouble finding any (clear) information on how to implement the syntax in my case.
My query:
SELECT pi.compressed_name, pi.phn, to_char(pi.date_of_birth , 'YYYY/MM/DD') as date_of_birth,
to_char(pe.started_on , 'YYYY/MM/DD' ) as medicare_eligibility_start,
to_char(pe.ended_on , 'YYYY/MM/DD' ) as medicare_eligibility_end
FROM medcrtr.forest_node fnpppp,
medcrtr.forest_node fnppp,
medcrtr.forest_node fnpp,
medcrtr.forest_node fnp,
medcrtr.forest_node fn,
medcrtr.group_member gm,
medcrtr.program_eligibility pe,
person_index pi
WHERE gm.entity_type_id = 1
AND fn.source_id = gm.group_id
AND fn.entity_type_id = 3
AND fnp.id = fn.parent_id
AND fnpp.id = fnp.parent_id
AND fnppp.id = fnpp.parent_id
AND fnpppp.id = fnppp.parent_id
AND pe.person_id = gm.source_id
AND pe.sub_program_id = fnpp.parent_id
AND pi.person_id = gm.source_id
AND fnppp.id = 1169
AND (gm.ended_on >= SYSDATE OR gm.ended_on IS NULL)
Can anyone point me in the right direction to get it converted to the different syntax?
I'm thinking something along the lines of:
SELECT pi.compressed_name, pi.phn, to_char(pi.date_of_birth , 'YYYY/MM/DD') as date_of_birth,
to_char(pe.started_on , 'YYYY/MM/DD' ) as medicare_eligibility_start,
to_char(pe.ended_on , 'YYYY/MM/DD' ) as medicare_eligibility_end
FROM medcrtr.forest_node fn,
group_member gm,
program_eligibility pe,
person_index pi
WHERE gm.entity_type_id = 1
AND fn.source_id = gm.group_id
AND fn.entity_type_id = 3
AND pe.person_id = gm.source_id
--AND pe.sub_program_id = fnpp.parent_id ???
AND pi.person_id = gm.source_id
--AND fnppp.id = 1169 ???
AND (gm.ended_on >= SYSDATE OR gm.ended_on IS NULL)
CONNECT BY PRIOR fn.id=fn.parent_id
This is not working obviously, and I don't know how to integrate the functionality of the fnpp...'s yet.Any help?

I'm not sure but I think you're missing the start with clause:
SELECT pi.compressed_name, pi.phn, to_char(pi.date_of_birth , 'YYYY/MM/DD') as date_of_birth,
to_char(pe.started_on , 'YYYY/MM/DD' ) as medicare_eligibility_start,
to_char(pe.ended_on , 'YYYY/MM/DD' ) as medicare_eligibility_end
FROM medcrtr.forest_node fn,
group_member gm,
program_eligibility pe,
person_index pi
WHERE gm.entity_type_id = 1
AND fn.source_id = gm.group_id
AND fn.entity_type_id = 3
AND pe.person_id = gm.source_id
AND pi.person_id = gm.source_id
AND (gm.ended_on >= SYSDATE OR gm.ended_on IS NULL)
start with fn.id = 1169 /*THis is where the recursion will start */
CONNECT BY PRIOR prior fn.id=fn.parent_id /*specify you want the current node's parent id to match the previous node's id*/
A good reference: http://www.adp-gmbh.ch/ora/sql/connect_by.html

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

how to use double group by and sum statements together in oracle

I am trying to use query below but it is giving an error
SELECT s.LOCAL_CODE,substr(p.ACCOUNT_CREDIT,-3),(p.SUMMA/100) as profit
FROM OPERATIONS s INNER JOIN LEADS p ON s.PAY_ID = p.PAY_ID
WHERE s.date_paid >= TO_DATE('03.12.2019', 'DD.MM.YYYY')
AND s.date_paid < TO_DATE('03.12.2019', 'DD.MM.YYYY') + INTERVAL '1' DAY
AND state = 'T'
AND s.filial_code = '006789'
AND SUBSTR(p.ACCOUNT_CREDIT, 1, 5) = '765294'
GROUP BY s.LOCAL_CODE,substr(p.ACCOUNT_CREDIT,-3);
If LEADS.SUMMA has expected value then you don't need Group By clause, else if you use Group By then all not grouped fields can be used only as arguments of aggregate functions:
SELECT s.LOCAL_CODE
, Substr(p.ACCOUNT_CREDIT, -3)
, Sum(p.SUMMA)/100 as profit
FROM OPERATIONS s
INNER JOIN LEADS p ON s.PAY_ID = p.PAY_ID
WHERE s.date_paid >= TO_DATE('03.12.2019', 'DD.MM.YYYY')
AND s.date_paid < TO_DATE('03.12.2019', 'DD.MM.YYYY') + INTERVAL '1' DAY
AND state = 'T'
AND s.filial_code = '006789'
AND SUBSTR(p.ACCOUNT_CREDIT, 1, 5) = '765294'
GROUP BY s.LOCAL_CODE
, substr(p.ACCOUNT_CREDIT, -3);

The query I ran returns 2 of the same column which isn't allowed in tableau and I can't fix the query

I need to be able to get rid of one of the workdate and one of the sr_name columns but I can't figure out how to.
I'm getting the query returned like this:
Query
I'm also getting this error message when entered into tableau:
The column 'sr_name' was specified multiple times for 'Custom SQL Query'.
Below is the code I have. If I remove a sr_name from either subquery there will be an error in the join clause.
select *
from
(
select s.sr_name, cast(punchdatetime as date) as workdate,
((datediff(second, min(case when p.InOut = 1 then punchdatetime end),
max(case when p.InOut = 0 then punchdatetime end))/3600) - .5) as
hoursworked
from PunchClock p join ServiceReps s on p.ServRepID = s.ServRepID
where punchyear >= 2019
group by s.sr_name, cast(punchdatetime as date)
) v
join
(
select sr_name, t.*,
calls = (select count(*) from CRM_Correspondence cr where
cast(cr.DateCreated as date) = workdate and StatusType like '%call%' and
cr.ServRepID = t.servrepid),
reaches = (select count(*) from CRM_Correspondence cr where
cast(cr.DateCreated as date) = workdate and (StatusType = 'call reached'
or StatusType like '%SCHEDULE%') and cr.ServRepID = t.servrepid),
books = (select count(*) from os_appointments o where cast(o.DateCreated
as date) = workdate and isnull(o.confirmedby, o.booked_by) =
t.servrepid),
attends = (select count(*) from os_appointments o where
cast(o.DateCreated as date) = workdate and isnull(o.confirmedby,
o.booked_by) = t.servrepid and o.appointmentStatus = 'attended')
from
(
select cast(cor.datecreated as date) workdate, cor.ServRepID
from CRM_Correspondence cor
where cor.datecreated > '2019-01-01'
group by cast(cor.datecreated as date), cor.servrepid
) t
join ServiceReps sr on t.ServRepID = sr.ServRepID
) u on v.sr_name = u.sr_name and v.workdate = u.workdate
I need the same results just without the duplicate column so I can enter the query into tableau.
This is challenging because you have so many subqueries here. This could be refactored to use a single query which is what I would do. But using the existing query you could do something along these lines.
I had to format this very differently so I could isolate each piece.
select v.sr_name
, v.workdate
, v.hoursworked
, u.ServRepID
, u.calls
, u.reaches
, u.books
, u.attends
from
(
select s.sr_name
, cast(punchdatetime as date) as workdate
, ((datediff(second, min(case when p.InOut = 1 then punchdatetime end), max(case when p.InOut = 0 then punchdatetime end))/3600) - .5) as hoursworked
from PunchClock p
join ServiceReps s on p.ServRepID = s.ServRepID
where punchyear >= 2019
group by s.sr_name
, cast(punchdatetime as date)
) v
join
(
select sr_name
, t.*
, calls =
(
select count(*)
from CRM_Correspondence cr
where cast(cr.DateCreated as date) = workdate
and StatusType like '%call%'
and cr.ServRepID = t.servrepid
)
, reaches =
(
select count(*)
from CRM_Correspondence cr
where cast(cr.DateCreated as date) = workdate
and (StatusType = 'call reached' or StatusType like '%SCHEDULE%')
and cr.ServRepID = t.servrepid
)
, books =
(
select count(*)
from os_appointments o
where cast(o.DateCreated as date) = workdate and isnull(o.confirmedby, o.booked_by) = t.servrepid
)
, attends =
(
select count(*)
from os_appointments o
where cast(o.DateCreated as date) = workdate
and isnull(o.confirmedby, o.booked_by) = t.servrepid
and o.appointmentStatus = 'attended'
)
from
(
select cast(cor.datecreated as date) workdate
, cor.ServRepID
from CRM_Correspondence cor
where cor.datecreated > '2019-01-01'
group by cast(cor.datecreated as date)
, cor.servrepid
) t
join ServiceReps sr on t.ServRepID = sr.ServRepID
) u on v.sr_name = u.sr_name
and v.workdate = u.workdate

No more spool space error - select statement

Spool space error
I ran the query and I get no more spool space in ID error. When I remove the 2 select statements (above the from statement), it runs fine. Need help optimizing the query please.
select NOT_FILETM_LOC_DTTM as documented_date, HNO_INFO.pat_enc_csn_id, AUTHOR_USER_ID, clarity_emp.name as md,patient.pat_id,hno_note_text.note_text, pat_mrn_id, cast(NOT_FILETM_LOC_DTTM as date) - cast( patient.birth_date as date) year (4) as age,
(select department_name from clarity_dep, clarity_adt where clarity_adt.department_id = dep.department_id and event_id =
(select max(event_id) from clarity_adt adt where pat_enc_hsp.pat_enc_csn_id = adt.pat_enc_csn_id and adt.EVENT_SUBTYPE_C <> 2 and adt.event_type_c<>6 and adt.effective_time <= NOT_FILETM_LOC_DTTM) ) as unit
from HNO_INFO, hno_note_text, NOTE_ENC_INFO, clarity_emp, patient, pat_enc_hsp
where OTE_ENC_INFO.note_id = HNO_INFO.note_id
and hno_note_text.note_id = HNO_INFO.note_id
and hno_note_text.note_text like ('%Procedural Sedation Stop Time:%')
and (hno_note_text.note_text like ('%PICC%') or hno_note_text.note_text like ('%Lumbar Puncture%'))
and emp.user_id = AUTHOR_USER_ID
and patient.pat_id = HNO_INFO.pat_id
and pat_enc_hsp.pat_enc_csn_id = HNO_INFO.pat_enc_csn_id
and cast(documented_date as date) >= '2018-01-01'
qualify row_number() over (partition by HNO_INFO.pat_enc_csn_id order by documented_date )=1
Okay, let's try this:
select
NOT_FILETM_LOC_DTTM as documented_date
, HNO_INFO.pat_enc_csn_id,
AUTHOR_USER_ID, emp."NAME" as md,patient.pat_id,hno_note_text.note_text, pat_mrn_id
, cast(NOT_FILETM_LOC_DTTM as date) - cast( patient.birth_date as date) year (4) as age
, department_name unit
, adt.event_id
from HNO_INFO
, hno_note_text
, NOTE_ENC_INFO
, clarity_emp emp
, patient
, pat_enc_hsp
,clarity_dep
--, clarity_adt
,(select max(event_id) event_id, adt.pat_enc_csn_id, department_id, effective_time from clarity_adt adt
where adt.EVENT_SUBTYPE_C <> 2 and adt.event_type_c<>6
group by adt.pat_enc_csn_id, department_id, effective_time
) adt
where
NOTE_ENC_INFO.note_id = HNO_INFO.note_id
and adt.department_id = clarity_dep.department_id
and pat_enc_hsp.pat_enc_csn_id = adt.pat_enc_csn_id
and adt.effective_time <= NOT_FILETM_LOC_DTTM
and hno_note_text.note_id = HNO_INFO.note_id
and hno_note_text.note_text like ('%Procedural Sedation Stop Time:%')
and (hno_note_text.note_text like ('%PICC%') or hno_note_text.note_text like ('%Lumbar Puncture%'))
and emp.user_id = AUTHOR_USER_ID
and patient.pat_id = HNO_INFO.pat_id
and pat_enc_hsp.pat_enc_csn_id = HNO_INFO.pat_enc_csn_id
and cast(documented_date as date) >= '2018-01-01'
qualify row_number() over (partition by HNO_INFO.pat_enc_csn_id order by documented_date )=1
;

Shorten SQL statement not using a common table expression

The SQL below is written to return 'open orders'. This was written in a way I could understand but now I would like to try and optimize this and reduce the amount of code.
The SQL below gives me the desired outcome I'm looking for however, I would like to shorten the query without using WITH AS. Any suggestions using UNION or some other nesting method?
WITH
product AS --filter by dept
(SELECT item
, dept
FROM item_master
WHERE dept in ('353')
),
open_orders AS --view of orders in Status A with ordered units > received units
(SELECT ol.order_no
, ol.item
, ol.location
, oh.po_type
, oh.order_type
, oh.not_before_date
, oh.not_after_date
, oh.otb_eow_date
, SUM(ol.qty_ordered) AS qty_ordered
, SUM(NVL(ol.qty_received,0)) AS qty_received
FROM ordhead oh
, ordloc ol
WHERE oh.order_no = ol.order_no
AND oh.status = 'A'
AND ol.qty_ordered > NVL(ol.qty_received,0)
-- AND ol.order_no in ('18701212') --optional filter for specific PO's
GROUP BY ol.order_no
, ol.item
, ol.location
, oh.po_type
, oh.order_type
, oh.not_before_date
, oh.not_after_date
, oh.otb_eow_date
),
allocations AS --view of all allocations
(SELECT ah.alloc_no
, ah.order_no
, ah.item
, ad.to_loc
, NVL(ad.qty_allocated,0) AS qty_allocated
, NVL(ad.qty_received,0) AS qty_received
FROM alloc_header ah
, alloc_detail ad
WHERE ah.alloc_no = ad.alloc_no
)
SELECT p.dept --main query on above views
, oo.order_no
, oo.po_type
, oo.order_type
, oo.not_before_date
, oo.not_after_date
, oo.otb_eow_date
, oo.item
, CASE WHEN oo.po_type = 0 THEN oo.location ELSE aa.to_loc END AS loc
, SUM(oo.qty_ordered) AS order_qty
, CASE WHEN SUM(NVL(aa.qty_allocated,0)) - SUM(NVL(aa.qty_received,0)) = 0
THEN SUM(oo.qty_ordered) - SUM(NVL(oo.qty_received,0))
ELSE SUM(NVL(aa.qty_allocated,0)) - SUM(NVL(aa.qty_received,0))
END AS open_qty
FROM open_orders oo
, allocations aa
, product p
WHERE oo.order_no = aa.order_no(+)
AND oo.item = aa.item(+)
AND oo.item = p.item
AND (oo.qty_ordered - oo.qty_received) >0
GROUP BY p.dept
, oo.order_no
, oo.po_type
, oo.order_type
, oo.not_before_date
, oo.not_after_date
, oo.otb_eow_date
, oo.item
, CASE WHEN oo.po_type = 0 THEN oo.location ELSE aa.to_loc END
;
CTE's (Common Table Expressions) are just a way of organizing a query by sticking bits of code (that define a "derived" table) at the top that can be reused in the main statement. As such, where product, open_orders, and allocations are mentioned in the FROM clause, you can just swap those words out with the code that defines them:
SELECT p.dept --main query on above views
,
oo.order_no,
oo.po_type,
oo.order_type,
oo.not_before_date,
oo.not_after_date,
oo.otb_eow_date,
oo.item,
CASE
WHEN oo.po_type = 0
THEN oo.location
ELSE aa.to_loc
END AS loc,
SUM(oo.qty_ordered) AS order_qty,
CASE
WHEN SUM(NVL(aa.qty_allocated, 0)) - SUM(NVL(aa.qty_received, 0)) = 0
THEN SUM(oo.qty_ordered) - SUM(NVL(oo.qty_received, 0))
ELSE SUM(NVL(aa.qty_allocated, 0)) - SUM(NVL(aa.qty_received, 0))
END AS open_qty
FROM (
SELECT ol.order_no,
ol.item,
ol.location,
oh.po_type,
oh.order_type,
oh.not_before_date,
oh.not_after_date,
oh.otb_eow_date,
SUM(ol.qty_ordered) AS qty_ordered,
SUM(NVL(ol.qty_received, 0)) AS qty_received
FROM ordhead oh,
ordloc ol
WHERE oh.order_no = ol.order_no
AND oh.STATUS = 'A'
AND ol.qty_ordered > NVL(ol.qty_received, 0)
-- AND ol.order_no in ('18701212') --optional filter for specific PO's
GROUP BY ol.order_no,
ol.item,
ol.location,
oh.po_type,
oh.order_type,
oh.not_before_date,
oh.not_after_date,
oh.otb_eow_date
) oo,
(
SELECT ah.alloc_no,
ah.order_no,
ah.item,
ad.to_loc,
NVL(ad.qty_allocated, 0) AS qty_allocated,
NVL(ad.qty_received, 0) AS qty_received
FROM alloc_header ah,
alloc_detail ad
WHERE ah.alloc_no = ad.alloc_no
) aa,
(
SELECT item,
dept
FROM item_master
WHERE dept IN ('353')
) p
WHERE oo.order_no = aa.order_no(+)
AND oo.item = aa.item(+)
AND oo.item = p.item
AND (oo.qty_ordered - oo.qty_received) > 0
GROUP BY p.dept,
oo.order_no,
oo.po_type,
oo.order_type,
oo.not_before_date,
oo.not_after_date,
oo.otb_eow_date,
oo.item,
CASE
WHEN oo.po_type = 0
THEN oo.location
ELSE aa.to_loc
END;
This is obviously not shortened (but by a few characters), but I get the sense that "shortening" isn't your requirement. You are trying to get this query to work in a product that doesn't support CTEs.