Sample Header Ad - 728x90

How to tune the complex Oracle stored procedure?

0 votes
1 answer
74 views
I have an Oracle stored procedure that takes a bunch of parameters and heavily does the left outer join like this:
create or replace PROCEDURE "SOME_PROCEDURE" (
	cRECORDSET OUT SYS_REFCURSOR,
	iPHY_YEAR IN SOME_PROCEDURE_TYPE.PHYSICAL_YEAR % TYPE,
	iPHYSICAL_DOC_TYPE IN SOME_PROCEDURE_TYPE.PHYSICAL_DOC_TYPE % TYPE,
	iPHYSICAL_DOC_TYPE_NONSAP IN SOME_PROCEDURE_TYPE.PHYSICAL_DOC_TYPE_NONSAP % TYPE,
	sREFERENCE_NO IN SOME_PROCEDURE_TYPE.REFERENCE_NO % TYPE,
	sPHYSICAL_NO IN SOME_PROCEDURE_TYPE.PHYSICAL_NO % TYPE,
	iLOCATION_ID IN SOME_PROCEDURE_TYPE.LOCATION_ID % TYPE,
	iCONTAINER_ID IN SOME_PROCEDURE_TYPE.CONTAINER_ID % TYPE,
	sCONTAINER_NUMBER IN SOME_PROCEDURE_TYPE.CONTAINER_NUMBER % TYPE,
	dREGISTERED_DATE_FROM IN SOME_PROCEDURE_TYPE.REGISTERED_DATE_FROM % TYPE,
	dREGISTERED_DATE_TO IN SOME_PROCEDURE_TYPE.REGISTERED_DATE_TO % TYPE,
	iPHYSICAL_STATUS_ID IN SOME_PROCEDURE_TYPE.PHYSICAL_STATUS_ID % TYPE,
	sDRAFT_FLAG IN SOME_PROCEDURE_TYPE.DRAFT_FLAG % TYPE,
	sCANCEL_FLAG IN SOME_PROCEDURE_TYPE.CANCEL_FLAG % TYPE,
	sPHYSICAL_REMARK IN SOME_PROCEDURE_TYPE.PHYSICAL_REMARK % TYPE,
	sPHYSICAL_DOC_TITLE IN SOME_PROCEDURE_TYPE.PHYSICAL_DOC_TITLE % TYPE,
	sFI_DOC_NO_FROM IN SOME_PROCEDURE_TYPE.FI_DOC_NO_FROM % TYPE,
	sFI_DOC_NO_TO IN SOME_PROCEDURE_TYPE.FI_DOC_NO_TO % TYPE,
	iCOMPANY_ID IN SOME_PROCEDURE_TYPE.COMPANY_ID % TYPE,
	iFI_DOC_YEAR IN SOME_PROCEDURE_TYPE.FI_DOC_YEAR % TYPE,
	sFI_DOC_TYPE IN SOME_PROCEDURE_TYPE.FI_DOC_TYPE % TYPE,
	sIS_ASSET IN SOME_PROCEDURE_TYPE.IS_ASSET % TYPE,
	sWORKFLOW_STATUS_CODE IN SOME_PROCEDURE_TYPE.WORKFLOW_STATUS_CODE % TYPE,
	sPO_NO IN SOME_PROCEDURE_TYPE.PO_NO % TYPE,
	sVENDOR_NAME IN SOME_PROCEDURE_TYPE.VENDOR_NAME % TYPE,
	sCUSTOMER_NAME IN SOME_PROCEDURE_TYPE.CUSTOMER_NAME % TYPE,
	dPOSTING_DATE_FROM IN SOME_PROCEDURE_TYPE.POSTING_DATE_FROM % TYPE,
	dPOSTING_DATE_TO IN SOME_PROCEDURE_TYPE.POSTING_DATE_TO % TYPE,
	sCLEARING_DOC_NO IN SOME_PROCEDURE_TYPE.CLEARING_DOC_NO % TYPE,
	iCLEARING_DOC_YEAR IN SOME_PROCEDURE_TYPE.CLEARING_DOC_YEAR % TYPE,
	dCLEARING_DOC_DATE IN SOME_PROCEDURE_TYPE.CLEARING_DOC_DATE % TYPE,
	sPAYMENT_BLOCK IN SOME_PROCEDURE_TYPE.PAYMENT_BLOCK % TYPE,
	iAMOUNT_INC_VAT IN SOME_PROCEDURE_TYPE.AMOUNT_INC_VAT % TYPE,
	sFI_DOC_REFNO IN SOME_PROCEDURE_TYPE.FI_DOC_REFNO % TYPE,
	sSEND_TO IN SOME_PROCEDURE_TYPE.SEND_TO % TYPE,
	sSAP_REVERSE_FLAG IN SOME_PROCEDURE_TYPE.SAP_REVERSE_FLAG % TYPE,
	sCLEARING_FLAG IN SOME_PROCEDURE_TYPE.CLEARING_FLAG % TYPE,
	iPAGE_SIZE IN SOME_PROCEDURE_TYPE.PAGE_SIZE % TYPE,
	iPAGE_INDEX IN SOME_PROCEDURE_TYPE.PAGE_INDEX % TYPE,
	iUSERID IN USR_DETAIL.USER_ID % TYPE
) IS
LAST_INDEX NUMBER;
USR_PERM_TYPE GET_USR_PERM_TYPE;
FIRST_INDEX NUMBER;
BEGIN
	LAST_INDEX := iPAGE_SIZE * iPAGE_INDEX;
	FIRST_INDEX := LAST_INDEX - iPAGE_SIZE + 1;
    SELECT FN_GET_USR_PERM(iUSERID) INTO USR_PERM_TYPE FROM DUAL;
	OPEN cRECORDSET FOR 
    
    WITH CTE_PHD_DETAIL AS (
    SELECT
        *
    FROM
        PHD_DETAIL
    WHERE
        USR_PERM_TYPE.VIS_MODE = 'ALL'
        OR (
            USR_PERM_TYPE.VIS_MODE = 'UNT'
            AND (UNIT_ID = USR_PERM_TYPE.USUB_ID)
        )
        OR (
            USR_PERM_TYPE.VIS_MODE = 'SUB'
            AND SUB_UNIT_ID = USR_PERM_TYPE.USUB_ID
        )
)
    
    SELECT
	PHD_ITEMS.RECORD_NUMBER,
	PHD.PHYSICAL_ID,
	PHD.PHYSICAL_DOC_TYPE,
	PHD.PHYSICAL_NO,
	PHD.REGISTERED_DATE,
	PHD.CANCEL_FLAG,
	PHD.DRAFT_FLAG,
	CASE
			WHEN PHD.REQUESTED_NO IS NOT NULL THEN
			PHD.REQUESTED_NO ELSE PHD.TEXT_FILE_NAME
		END REFERENCE_NO,
	PHDS.PHYSICAL_STATUS_ID,
	PHDS.PHYSICAL_STATUS_NAME,
	CNT.CONTAINER_ID,
CASE
	WHEN PHD.PHYSICAL_STATUS_ID = 2 THEN
	NULL ELSE CNT.CONTAINER_NUMBER
	END CONTAINER_NUMBER,
	CNT.CONTAINER_TYPE,
	CNTU.IS_DOC_HOUSE CONTAINER_IS_DOC_HOUSE,
	CNTU.UNIT_ABBR CONTAINER_UNIT_ABBR,
	LOC.LOCATION_ID,
CASE
	WHEN PHD.PHYSICAL_STATUS_ID = 2
	OR PHD.PHYSICAL_STATUS_ID = 12
	OR PHD.PHYSICAL_STATUS_ID = 14 THEN
	(
	SELECT
		SEND_TO
	FROM
		( SELECT SEND_TO, PHYSICAL_ID FROM PHD_STATUS_LOGS T1 WHERE T1.PHYSICAL_STATUS_ID = 2 ORDER BY CREATE_DATE DESC )
	WHERE
		ROWNUM = 1
		AND PHYSICAL_ID = PHD_ITEMS.PHYSICAL_ID
	) --DECODE( PHDLG.SENT_TO,NULL , '',PHDLG.SENT_TO )
	WHEN CNT.CONTAINER_TYPE = 1 THEN
CASE
		WHEN CNT.CONTAINER_STATUS_ID = 2 THEN
	CASE
			WHEN CNT.CURRENT_UNIT_ID IS NOT NULL THEN
			CNTU.UNIT_FULL_NAME ELSE CCOM.COMPANY_CODE || ' - ' || CCOM.COMPANY_NAME
		END ELSE LOC.LOCATION_NAME
	END ELSE LOC.LOCATION_NAME -- LOC.LOCATION_NAME
	END LOCATION_NAME,
	FIDS.WORKFLOW_STATUS_NAME,
	FID.FI_DOC_ID,
	FID.FI_DOC_NO,
	FID.FI_DOC_YEAR,
CASE
	WHEN COM_FID.COMPANY_ID IS NOT NULL THEN
	COM_FID.COMPANY_CODE || ' - ' || COM_FID.COMPANY_NAME ELSE
CASE
		WHEN COM_PHD.COMPANY_ID IS NOT NULL THEN
		COM_PHD.COMPANY_CODE || ' - ' || COM_PHD.COMPANY_NAME ELSE ''
	END
	END AS COMPANY_FULL_NAME,
	FID.FI_DOC_REFNO,
CASE
		WHEN FID.FI_DOC_ID IS NULL THEN
		PTEMP.AMOUNT_INC_VAT ELSE FID.AMOUNT_INC_VAT
	END AMOUNT_INC_VAT,
CASE
	WHEN FID.FI_DOC_ID IS NULL THEN
	PTEMP.AMOUNT_CURRENCY ELSE FID.AMOUNT_CURRENCY
	END AMOUNT_CURRENCY,
	FID.IS_ASSET,
CASE
	WHEN PHD_ITEMS.FI_DOC_ID IS NULL THEN
CASE
		WHEN PTEMP.CUSTOMER_CODE IS NOT NULL THEN
		PTCUS.CUSTOMER_FULL_NAME ELSE PTVEN.VENDOR_FULL_NAME
END ELSE
CASE
		WHEN FID.FIRST_ITEMS = 'CUSTOMER' THEN
		FID.CUSTOMER_NAME ELSE FID.VENDOR_NAME
	END
	END AS CUSTOMER_VENDOR_NAME,
	PHD.PHYSICAL_REMARK
FROM
	(
	SELECT
		ROW_NUMBER ( ) over ( ORDER BY PHD.PHYSICAL_NO DESC, FID_ID.FI_DOC_NO, PHDTEMP.PHD_TEMP_DATA_ID DESC ) RECORD_NUMBER,
		PHD.PHYSICAL_ID,
		PHD.PHYSICAL_NO,
		FID_ID.FI_DOC_ID,
		FID_ID.FI_DOC_NO,
		PHDTEMP.PHD_TEMP_DATA_ID
	FROM
		(
		SELECT DISTINCT
			PHD.PHYSICAL_ID,
			PHD.PHYSICAL_NO,
			FID_ID.FI_DOC_ID,
			PHDTEMP.PHD_TEMP_DATA_ID
		FROM
			CTE_PHD_DETAIL PHD
			LEFT JOIN (
			SELECT
				t1.FI_DOC_ID,
				t1.PHYSICAL_ID,
				t2.fi_Doc_no,
				t2.fi_doc_year
			FROM
				PHD_FID_RELATE_DOC t1
				INNER JOIN FID_SAP_DETAIL t2 ON t1.FI_DOC_ID = t2.FI_DOC_ID UNION ALL
			SELECT
				t1.FI_DOC_ID,
				t1.PHYSICAL_ID,
				t2.fi_Doc_no,
				t2.fi_doc_year
			FROM
				FID_SAP_PHYSICAL_HISTORY t1
				INNER JOIN FID_SAP_DETAIL t2 ON t1.FI_DOC_ID = t2.FI_DOC_ID
			) FID_ID ON FID_ID.PHYSICAL_ID = PHD.PHYSICAL_ID
			LEFT JOIN PHD_TEMP_DATA PHDTEMP ON PHD.PHYSICAL_ID = PHDTEMP.PHYSICAL_ID
			AND FID_ID.FI_DOC_ID
			IS NULL LEFT JOIN PHD_STATUS_LOGS PHDSL ON PHD.PHYSICAL_ID = PHDSL.PHYSICAL_ID
			LEFT JOIN PHD_TEMP_DOCTITLE PHDT ON PHD.PHYSICAL_ID = PHDT.PHYSICAL_ID
			LEFT JOIN CNT_DETAIL CNT ON PHD.CURRENT_CONTAINER_ID = CNT.CONTAINER_ID
			LEFT JOIN VEW_UNT_DETAIL CNTU ON CNT.CURRENT_UNIT_ID = CNTU.UNIT_ID
			LEFT JOIN LOC_DETAIL LOC ON CNT.LOCATION_ID = LOC.LOCATION_ID
			LEFT JOIN COM_DETAIL COM_PHD ON PHD.COMPANY_ID = COM_PHD.COMPANY_ID
			LEFT JOIN FID_SAP_DETAIL FID ON FID_ID.FI_DOC_ID = FID.FI_DOC_ID
			LEFT JOIN COM_DETAIL COM ON FID.COMPANY_CODE = COM.COMPANY_CODE
			LEFT JOIN FID_SAP_CUSTOMER FCUS ON FID.FI_DOC_ID = FCUS.FI_DOC_ID
			LEFT JOIN FID_SAP_VENDOR FVEN ON FID.FI_DOC_ID = FVEN.FI_DOC_ID
			LEFT JOIN FID_SAP_CLEARING_DOC CLR ON FID.FI_DOC_ID = CLR.FI_DOC_ID
			LEFT JOIN FID_SAP_PO PO ON FID.FI_DOC_ID = PO.FI_DOC_ID
			LEFT JOIN FID_SAP_STATUS FIDS ON FID.WORKFLOW_STATUS_CODE = FIDS.WORKFLOW_STATUS_CODE
			LEFT JOIN MAS_VENDOR PTVEN ON PHDTEMP.VENDOR_CODE = PTVEN.VENDOR_CODE
			AND COM_PHD.SERVER_ID = COM_PHD.SERVER_ID
			LEFT JOIN MAS_CUSTOMER PTCUS ON PHDTEMP.CUSTOMER_CODE = PTCUS.CUSTOMER_CODE
			AND PTCUS.SERVER_ID = COM_PHD.SERVER_ID
		WHERE
			PHD.PHYSICAL_NO IS NOT NULL
			AND (
				(
					(
						( PHD.PHYSICAL_DOC_TYPE = 1 AND iPHYSICAL_DOC_TYPE = 1 )
						OR ( ( PHD.PHYSICAL_DOC_TYPE = 2 OR PHD.PHYSICAL_DOC_TYPE = 4 ) AND iPHYSICAL_DOC_TYPE = 2 )
					)
					AND iPHYSICAL_DOC_TYPE_NONSAP IS NULL
				)
				OR ( PHD.PHYSICAL_DOC_TYPE = iPHYSICAL_DOC_TYPE_NONSAP AND iPHYSICAL_DOC_TYPE_NONSAP IS NOT NULL )
			)
			AND (
				sREFERENCE_NO IS NULL
				OR (
					UPPER( PHD.REQUESTED_NO ) LIKE '%' || UPPER( sREFERENCE_NO ) || '%'
					OR UPPER( PHD.TEXT_FILE_NAME ) LIKE '%' || UPPER( sREFERENCE_NO ) || '%'
				)
			)
			AND ( sPHYSICAL_NO IS NULL OR ( PHD.PHYSICAL_NO = sPHYSICAL_NO ) )
			AND ( iLOCATION_ID IS NULL OR ( LOC.LOCATION_ID = iLOCATION_ID ) )
			AND ( iPHYSICAL_STATUS_ID IS NULL OR ( PHD.PHYSICAL_STATUS_ID = iPHYSICAL_STATUS_ID ) )
			AND ( iCONTAINER_ID IS NULL OR ( CNT.CONTAINER_ID = iCONTAINER_ID AND CNT.CONTAINER_TYPE = 2 ) )
			AND (
				sCONTAINER_NUMBER IS NULL
				OR ( UPPER( CNT.CONTAINER_NUMBER ) LIKE '%' || UPPER( sCONTAINER_NUMBER ) || '%' AND CNT.CONTAINER_TYPE = 1 )
			)
			AND ( TRUNC( dREGISTERED_DATE_FROM ) IS NULL OR ( TRUNC( PHD.REGISTERED_DATE ) >= TRUNC( dREGISTERED_DATE_FROM ) ) )
			AND ( TRUNC( dREGISTERED_DATE_TO ) IS NULL OR ( TRUNC( PHD.REGISTERED_DATE ) = sFI_DOC_NO_FROM ) )
			AND ( sFI_DOC_NO_TO IS NULL OR ( FID.FI_DOC_NO = TRUNC( dPOSTING_DATE_FROM ) OR TRUNC( PHDTEMP.CREATE_DATE ) = TRUNC( dPOSTING_DATE_FROM ) )
			)
			AND ( sCLEARING_DOC_NO IS NULL OR ( UPPER( CLR.CLEARING_DOC_NO ) LIKE '%' || UPPER( sCLEARING_DOC_NO ) || '%' ) )
			AND ( iCLEARING_DOC_YEAR IS NULL OR ( CLR.CLEARING_DOC_YEAR = iCLEARING_DOC_YEAR ) )
			AND ( dCLEARING_DOC_DATE IS NULL OR ( CLR.CLEARING_DATE = dCLEARING_DOC_DATE ) ) --AND ( sPAYMENT_BLOCK      IS NULL
-- OR ( UPPER(FID.PAYMENT_BLOCK) LIKE '%'
--   || UPPER(sPAYMENT_BLOCK)
--  || '%' ) )
			AND ( sFI_DOC_REFNO IS NULL OR ( UPPER( FID.FI_DOC_REFNO ) LIKE '%' || UPPER( sFI_DOC_REFNO ) || '%' ) )
			AND ( ( sSAP_REVERSE_FLAG IS NULL AND ( FID.SAP_REVERSE_FLAG IS NULL OR FID.FI_DOC_ID IS NULL ) ) OR ( sSAP_REVERSE_FLAG IS NOT NULL ) )
			AND ( iAMOUNT_INC_VAT IS NULL OR ( FID.AMOUNT_INC_VAT = iAMOUNT_INC_VAT OR PHDTEMP.AMOUNT_INC_VAT = iAMOUNT_INC_VAT ) )
			AND (
				sCUSTOMER_NAME IS NULL
				OR UPPER( PTCUS.CUSTOMER_FULL_NAME ) LIKE '%' || UPPER( sCUSTOMER_NAME ) || '%'
				OR UPPER( FCUS.CUSTOMER_CODE || ' ' || FCUS.CUSTOMER_NAME ) LIKE '%' || UPPER( sCUSTOMER_NAME ) || '%'
			)
			AND (
				sVENDOR_NAME IS NULL
				OR UPPER( PTVEN.VENDOR_FULL_NAME ) LIKE '%' || UPPER( sVENDOR_NAME ) || '%'
				OR UPPER( FVEN.VENDOR_CODE || ' ' || FVEN.VENDOR_NAME ) LIKE '%' || UPPER( sVENDOR_NAME ) || '%'
			) --AND FID_ID.FI_DOC_ID IS NULL AND PHDTEMP.PHD_TEMP_DATA_ID IS NOT NULL
		) PHD
		LEFT JOIN FID_SAP_DETAIL FID_ID ON FID_ID.FI_DOC_ID = PHD.FI_DOC_ID
		LEFT JOIN PHD_TEMP_DATA PHDTEMP ON PHD.PHD_TEMP_DATA_ID = PHDTEMP.PHD_TEMP_DATA_ID
		AND PHD.FI_DOC_ID IS NULL
	) PHD_ITEMS
	INNER JOIN CTE_PHD_DETAIL PHD ON PHD_ITEMS.PHYSICAL_ID = PHD.PHYSICAL_ID
	LEFT JOIN PHD_STATUS PHDS ON PHD.PHYSICAL_STATUS_ID = PHDS.PHYSICAL_STATUS_ID
	LEFT JOIN CNT_DETAIL CNT ON PHD.CURRENT_CONTAINER_ID = CNT.CONTAINER_ID
	LEFT JOIN VEW_UNT_DETAIL CNTU ON CNT.CURRENT_UNIT_ID = CNTU.UNIT_ID
	LEFT JOIN LOC_DETAIL LOC ON CNT.LOCATION_ID = LOC.LOCATION_ID
	LEFT JOIN COM_DETAIL CCOM ON CNT.CURRENT_COMPANY_ID = CCOM.COMPANY_ID
	LEFT JOIN COM_DETAIL COM_PHD ON PHD.COMPANY_ID = COM_PHD.COMPANY_ID
	LEFT JOIN FID_SAP_DETAIL FID ON PHD_ITEMS.FI_DOC_ID = FID.FI_DOC_ID
	LEFT JOIN FID_SAP_STATUS FIDS ON FID.WORKFLOW_STATUS_CODE = FIDS.WORKFLOW_STATUS_CODE
	LEFT JOIN COM_DETAIL COM_FID ON COM_FID.COMPANY_CODE = FID.COMPANY_CODE
	LEFT JOIN PHD_TEMP_DATA PTEMP ON PHD_ITEMS.PHD_TEMP_DATA_ID = PTEMP.PHD_TEMP_DATA_ID
	LEFT JOIN MAS_VENDOR PTVEN ON PTEMP.VENDOR_CODE = PTVEN.VENDOR_CODE
	AND PHD_ITEMS.FI_DOC_ID IS NULL
	AND PTVEN.SERVER_ID = COM_PHD.SERVER_ID
	LEFT JOIN MAS_CUSTOMER PTCUS ON PTEMP.CUSTOMER_CODE = PTCUS.CUSTOMER_CODE
	AND PHD_ITEMS.FI_DOC_ID IS NULL
	AND PTCUS.SERVER_ID = COM_PHD.SERVER_ID
WHERE
	PHD_ITEMS.RECORD_NUMBER BETWEEN FIRST_INDEX
	AND LAST_INDEX
ORDER BY
	RECORD_NUMBER;
END;
This query takes around 1:30 minutes before issuing the ~4000 records query result. It is hard to extract the execution plan for this long query. What suggestion or strategy or should I do something to get the plan? In my opinion, I currently have 3 options: ## Dynamic Query The first option is escalating the procedure to be ORM statement with some if statement to compose the proper statement with inner join instead of left outer join (if the parameter is null, no join). I think this option is smart when I think about at a first glance. However, it might took me so long time to accomplish the task. ## Materialized View One of option that I think about is pulling the left join clauses to be rewritten as Materialized View. Unfortunately, this store procedure is involved in application so the Materialized View will be updated frequently when combine with ON COMMIT or ON STATEMENT. Is this option considered dangerous for deadlock or bad performance? ## Remove some search criteria (stored procedure parameters) Suppose that I can remove some criteria, it means I can remove some left outer join clauses. However, this option is not possible after I discuss with teammate. How do I suppose to do to tackle with performance problem or what other strategies or options do I have? Any advices are all welcomed.
Asked by Pranithan T. (103 rep)
Jan 7, 2025, 10:22 AM
Last activity: Jan 7, 2025, 07:42 PM