Optimize Insert statement - oracle

I have an insert block which inserts almost 5680969 rows and takes about 10 mins to execute. I tried to optimize using parallel hints and other things like summarizing queries. Can this block of query can be further optimized?? Any help will be much appreciated.
DECLARE
vblQueryName VARCHAR2(20);
BEGIN
vblQueryName:='060_745_085';
INSERT /*+ APPEND */ INTO TABLE_A
(
SOURCE,
SN,
CLMNUM,
CLAIMLINENUMBER,
CLMTYPE,
CLMTYPEDESC,
CLMCATEGORY,
MEMID,
ENRID,
RELFLAG,
MEMFIRSTNAME,
MEMLASTNAME,
GENDER,
DOB,
ADDR1,
ADDR2,
CITY,
STATE,
ZIP,
HOMEPHONE,
WORKPHONE,
LVLID1,
LVLDESC1,
LVLID2,
LVLDESC2,
LVLID3,
LVLDESC3,
LVLID4,
LVLDESC4,
LVLID5,
LVLDESC5,
LVLID6,
LVLDESC6,
LVLID7,
LVLDESC7,
LVLID8,
LVLDESC8,
LVLID9,
LVLDESC9,
LVLID10,
LVLDESC10,
FROMDATE,
TODATE,
SERVICEDATE,
RCVDATE,
PAIDDATE,
BILLTYPE,
POSCODE,
POSDESC,
SPECCODE,
SPECDESC,
DIAGCODE,
DIAGDESC,
FIRSTDIAGCODE,
FIRSTDIAGDESC,
SECONDDIAGCODE,
SECONDDIAGDESC,
THIRDDIAGCODE,
THIRDDIAGDESC,
FOURTHDIAGCODE,
FOURTHDIAGDESC,
FIFTHDIAGCODE,
FIFTHDIAGDESC,
SIXTHDIAGCODE,
SIXTHDIAGDESC,
SEVENTHDIAGCODE,
SEVENTHDIAGDESC,
EIGHTHDIAGCODE,
EIGHTHDIAGDESC,
NINTHDIAGCODE,
NINTHDIAGDESC,
TENTHDIAGCODE,
TENTHDIAGDESC,
PROCTYPE,
PROCCODE,
PROCDESC,
REVCODE,
DRGCODE,
MODIFIERCODE,
MODIFIERDESC,
CPT4_1,
CPT4_2,
CPT4_3,
HCPCS,
CPTII,
MODIFIERCODE2,
REVCODE1,
REVCODE2,
REVCODE4,
REVCODE3,
REVCODE5,
ICD9PROCCODE1,
ICD9PROCCODE2,
ICD9PROCCODE3,
ICD9PROCCODE4,
ICD9PROCCODE5,
ICD9PROCCODE6,
DRGTYPE,
DRGIDENTIFIER,
IPDAYS,
DISCHSTATUS,
TYPEOFBILL,
CLAIMSTATUS,
ADJCODE,
PROVID,
PROVNAME,
PROVIDERFIRSTNAME,
PROVIDERLASTNAME,
PROVNPI,
PROVZIPCODE,
SERVTYPECODE,
SERVTYPEDESC,
PROVTYPECODE,
PROVTYPEDESC,
SERVICECODE,
SPECROLLUPCODE,
SPECROLLUPDESC,
NWKID,
NWKNAME,
INNWK,
NETWORKTYPE,
SERVICEUNITS,
PAIDAMT,
BILLEDAMT,
ALLOWEDAMT,
PPOSAVINGAMT,
ENRPAIDAMT,
COINSAMT,
COPAYAMT,
DEDUCTAMT,
NOTALLOWEDAMT,
COBAMT,
PLANEXCLAMT,
LABTESTDATA,
SICCODE,
SICDESC,
SSN,
RCVMTH,
SRCFILENAME,
UDF1,
UDFC10,
UDFc19,
UDFc20,
ICDTYPE,
VHPAYORID
)
SELECT /*+PARALLEL(a,8) */
'SOURCE' AS SOURCE,
ROWNUM AS SN,
CASE UPPER(a.PROVIDER_NETWORK_PAR_INDICATOR) WHEN 'Y' THEN 'Y_' ELSE 'N_' END || a.CLAIM_NUMBER || a.CLAIM_LINE_NUMBER || ROWNUM AS CLMNUM,
a.CLAIM_LINE_NUMBER AS CLAIMLINENUMBER,
'MED' AS CLMTYPE,
'MEDICAL' AS CLMTYPEDESC ,
NULL AS CLMCATEGORY,
a.MEMID AS MEMID,
a.SUBSCRIBER_NUMBER AS ENRID,
NULL AS RELFLAG ,
UPPER(a.MEMBER_FIRST_NAME) AS MEMFIRSTNAME,
UPPER(a.MEMBER_LAST_NAME) AS MEMLASTNAME,
UPPER(a.MEMBER_GENDER) AS GENDER,
a.MEMBER_DATE_OF_BIRTH AS DOB,
a.MEMBER_ADDRESS_1 AS ADDR1,
a.MEMBER_ADDRESS_2 AS ADDR2,
a.MEMBER_CITY AS CITY,
a.MEMBER_STATE AS STATE,
a.MEMBER_ZIP AS ZIP,
a.MEMBER_PHONE AS HOMEPHONE,
NULL AS WORKPHONE,
'SOURCE' AS LVLID1,
'SOURCE' AS LVLDESC1,
NULL AS LVLID2,
NULL AS LVLDESC2,
NVL(lvl.VRSK_EMPLR_GRPID, REGEXP_REPLACE(a.subgroup_number,'[^a-zA-Z0-9]')) AS LVLID3,
NVL(lvl.EMPLOYER_GROUP_NM, REGEXP_REPLACE(a.subgroup_number,'[^a-zA-Z0-9]')) AS LVLDESC3,
NULL AS LVLID4,
NULL AS LVLDESC4,
NULL AS LVLID5,
NULL AS LVLDESC5,
NULL AS LVLID6,
NULL AS LVLDESC6,
NULL AS LVLID7,
NULL AS LVLDESC7,
NULL AS LVLID8,
NULL AS LVLDESC8,
NULL AS LVLID9,
NULL AS LVLDESC9,
NULL AS LVLID10,
NULL AS LVLDESC10,
a.SERVICE_START_DATE AS FROMDATE,
a.SERVICE_END_DATE AS TODATE,
a.SERVICE_START_DATE AS SERVICEDATE,
a.CLAIM_RECEIVED_DATE AS RCVDATE,
a.CLAIM_PROCESS_DATE AS PAIDDATE,
Decode(a.ENCOUNTER_TYPE_CODE, 'FCLTY', 'F', 'PROF', 'P', 'DENT', 'P') AS BILLTYPE,
a.PLACE_OF_SERVICE_CODE AS POSCODE,
NULL AS POSDESC,
e.ROLLUP_SPECCODE AS SPECCODE,
e.ROLLUP_SPECDESC AS SPECDESC,
COALESCE(a.PRIMARY_DIAGNOSIS_CODE,a.DIAGNOSIS_CODE_2,a.DIAGNOSIS_CODE_3,a.DIAGNOSIS_CODE_4,a.DIAGNOSIS_CODE_5) AS DIAGCODE,
NULL AS DIAGDESC,
a.PRIMARY_DIAGNOSIS_CODE AS FIRSTDIAGCODE,
NULL AS FIRSTDIAGDESC,
a.DIAGNOSIS_CODE_2 AS SECONDDIAGCODE,
NULL AS SECONDDIAGDESC,
a.DIAGNOSIS_CODE_3 AS THIRDDIAGCODE,
NULL AS THIRDDIAGDESC,
a.DIAGNOSIS_CODE_4 AS FOURTHDIAGCODE,
NULL AS FOURTHDIAGDESC,
a.DIAGNOSIS_CODE_5 AS FIFTHDIAGCODE,
NULL AS FIFTHDIAGDESC,
NULL AS SIXTHDIAGCODE,
NULL AS SIXTHDIAGDESC,
NULL AS SEVENTHDIAGCODE,
NULL AS SEVENTHDIAGDESC,
NULL AS EIGHTHDIAGCODE,
NULL AS EIGHTHDIAGDESC,
NULL AS NINTHDIAGCODE,
NULL AS NINTHDIAGDESC,
NULL AS TENTHDIAGCODE,
NULL AS TENTHDIAGDESC,
NULL AS PROCTYPE,
COALESCE(proc1.PROCCODE, proc2.PROCCODE, proc3.PROCCODE,a.PROCEDURE_CODE, NULLIF('I'||a.ICD_PROCEDURE_CODE_1,'I'), NULLIF('D'||a.AP_DRG,'D')) AS PROCCODE,
NULL AS PROCDESC,
CASE WHEN proc2.procTypeDesc='Rev Code' THEN proc2.PROCCODE END AS REVCODE,
proc3.PROCCODE AS DRGCODE,
NULL AS MODIFIERCODE,
NULL AS MODIFIERDESC,
CASE WHEN proc2.procTypeDesc='CPT4' THEN proc2.PROCCODE END AS CPT4_1,
NULL AS CPT4_2,
NULL AS CPT4_3,
CASE WHEN proc2.procTypeDesc='HCPCS' THEN proc2.PROCCODE END AS HCPCS,
NULL AS CPTII,
NULL AS MODIFIERCODE2,
CASE WHEN proc2.procTypeDesc='Rev Code' THEN proc2.PROCCODE END AS REVCODE1,
NULL AS REVCODE2,
NULL AS REVCODE4,
NULL AS REVCODE3,
NULL AS REVCODE5,
proc1.PROCCODE AS ICD9PROCCODE1,
REPLACE(a.ICD_PROCEDURE_CODE_2, '.') AS ICD9PROCCODE2,
REPLACE(a.ICD_PROCEDURE_CODE_3, '.') AS ICD9PROCCODE3,
NULL AS ICD9PROCCODE4,
NULL AS ICD9PROCCODE5,
NULL AS ICD9PROCCODE6,
NULL AS DRGTYPE,
NULL AS DRGIDENTIFIER,
NULL AS IPDAYS,
a.DISCHARGE_STATUS_CODE AS DISCHSTATUS,
NULL AS TYPEOFBILL,
NULL AS CLAIMSTATUS,
NULL AS ADJCODE,
coalesce(prov.rollupproviderid, a.provider_number) AS PROVID,
coalesce(prov.rollupprovidername,a.provider_name) AS PROVNAME,
NULL AS PROVIDERFIRSTNAME,
NULL AS PROVIDERLASTNAME,
NULL AS PROVNPI,
SubStr(a.PROVIDER_ZIP, 1, 5) AS PROVZIPCODE,
NULL AS SERVTYPECODE,
NULL AS SERVTYPEDESC,
NULL AS PROVTYPECODE,
NULL AS PROVTYPEDESC,
NULL AS SERVICECODE,
NULL AS SPECROLLUPCODE,
NULL AS SPECROLLUPDESC,
NVL(d.ROLLUPNWKID,a.PROVIDER_REGION_CODE) AS NWKID,
COALESCE(d.ROLLUPNWKNAME,a.PROVIDER_REGION_CODE) AS NWKNAME,
CASE UPPER(a.PROVIDER_NETWORK_PAR_INDICATOR) WHEN 'Y' THEN 'Y' ELSE 'N' END AS INNWK,
NULL AS NETWORKTYPE,
a.SERVICE_UNIT_COUNT AS SERVICEUNITS,
Nvl(a.PAID_AMOUNT,0) AS PAIDAMT,
Nvl(a.CHARGED_AMOUNT,0) AS BILLEDAMT,
Nvl(a.CHARGED_AMOUNT *0.54,0) AS ALLOWEDAMT,
NULL AS PPOSAVINGAMT,
NVL(a.COPAY_AMOUNT,0) + NVL(a.COINSURANCE_AMOUNT,0) + NVL(a.DEDUCTIBLE_AMOUNT,0) AS ENRPAIDAMT, -- corrected on /5/30/2013
Nvl(a.COINSURANCE_AMOUNT,0) AS COINSAMT,
Nvl(a.COPAY_AMOUNT,0) AS COPAYAMT,
Nvl(a.DEDUCTIBLE_AMOUNT,0) AS DEDUCTAMT,
Nvl(a.NOT_COVERED_AMOUNT,0) AS NOTALLOWEDAMT,
Nvl(a.COB_AMOUNT,0) AS COBAMT,
NULL AS PLANEXCLAMT,
NULL AS LABTESTDATA,
NULL AS SICCODE,
NULL AS SICDESC,
NULL AS SSN,
a.RECEIVEDMONTH AS RCVMTH,
a.SOURCEFILENAME AS SRCFILENAME,
'BCBSNC' AS UDF1 ,
a.SUBSCRIBER_NUMBER|| TO_CHAR(a.MEMBER_DATE_OF_BIRTH,'YYYYMMDD') AS UDFC10,--ICE176721
a.subgroup_number AS UDFc19,
a.benefit_package_id AS UDFc20,
CASE WHEN a.SERVICE_START_DATE>=to_date('20151001','YYYYMMDD') THEN 'ICD10' else 'ICD9' end as ICDTYPE,
payor.PAYORID AS VHPAYORID
FROM
HI0000001.HI_CLAIMS_SOURCE a
LEFT JOIN
HR_745_LOA_SOURCE lvl
ON
a.SUBGROUP_NUMBER = lvl.SUBGROUP_ID
LEFT JOIN
HR_745_NWK_CIGNA d
ON
a.PROVIDER_REGION_CODE = d.NWKID
AND
d.SOURCE = 'SOURCE'
LEFT JOIN
HR_745_SPEC_MERGED e
ON
a.PROVIDER_SPECIALTY_CODE = e.SRC_SPECCODE
AND
e.PAYER='BCBSNC'
LEFT JOIN
zzz_procs proc1
ON
'I'||REPLACE(a.ICD_PROCEDURE_CODE_1,'.') = proc1.proccode
LEFT JOIN
zzz_procs proc2
ON
CASE WHEN LENGTH(a.PROCEDURE_CODE)=4 AND SUBSTR(a.PROCEDURE_CODE,1,1)='0' THEN 'R'||SUBSTR(a.PROCEDURE_CODE,-3) ELSE a.PROCEDURE_CODE END = proc2.PROCCODE
LEFT JOIN
zzz_procs proc3
ON
'D'|| a.AP_DRG = proc3.proccode
LEFT JOIN
HR_GLOBAL_PAYORLIST payor
ON
payor.TABLENAME = 'HI_CLAIMS_BCBSNC'
LEFT JOIN
HR_745_PROVIDER prov
ON
'SOURCE' = prov.SOURCE
AND
UPPER(COALESCE(a.provider_number,'NULL')) = prov.provid
AND
UPPER(Nvl(a.provider_name, 'NULL')) = prov.provname
WHERE
a.ENCOUNTER_SERVICE_TYPE_CODE NOT IN ('06','02') AND a.ENCOUNTER_TYPE_CODE <>'CP'
AND
REGEXP_REPLACE(a.subgroup_number,'[^a-zA-Z0-9]') IS NOT NULL
AND
NVL(lvl.VRSK_EMPLR_GRPID, a.subgroup_number) NOT IN
(SELECT drp.lvl3id FROM hr_745_lvl3_drop drp WHERE source='BCBSNC')
AND
(NVL(lvl.VRSK_EMPLR_GRPID, a.subgroup_number)<>'539431' OR a.CLAIM_PROCESS_DATE<LAST_DAY(TO_DATE('2016-09','yyyy-mm')));
COMMIT;
DBMS_OUTPUT.PUT_LINE( 'Query Executed: ' || vblqueryName);
INSERT INTO VH_QUERYLOG(QueryName,CURTIME) VALUES(vblQueryName,SYSDATE);
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
/

Add this statement before the INSERT:
execute immediate 'alter session enable parallel dml';
Remove all the hints and only use this one hint at the top:
/*+ APPEND PARALLEL */
This allows the entire statement to run in parallel, not just certain parts of the SELECT. Unless you're using an old, unsupported version of Oracle, you normally want to avoid listing the objects in the parallel hint. Using the parallel hint without a number will hopefully pick a better degree of parallelism, depending on the way the system is setup. If that doesn't work out, then I'd recommend trying different numbers. (In short, a higher DOP is always faster, but there are diminishing returns and it can steal resources from other statements.)
After you've done that, take a look at the explain plan for the INSERT. Ensure it's using LOAD AS SELECT and not CONVENTIONAL INSERT - that's how you tell the statement is using a direct-path write. The APPEND hint is tricky and there are many things that may prevent it from working correctly.
Then run the code and generate a SQL Monitor report like this: select dbms_sqltune.report_sql_monitor(sql_id => 'the insert SQL_ID') from dual;. That report will tell you which operation in the query is slow, if any. The explain plan will probably generate a few dozen lines, and without the SQL Monitor report you'll have to guess which part is slow. Some of the join conditions look complicated, I wouldn't be surprised if you see a few operations where the estimated rows are 1 but the actual are much higher, leading to NESTED LOOPs instead of HASH JOINs.
Those are high-level hints for getting started with a parallel INSERT. You can easily spend hours on a statement like this.

Related

why count(*) is slow even with index?

This is my query:
select count(*)
FROM TB_E2V_DOCUMENTOS_CICLO D
WHERE (D.TIPOCLIENTE = null or null is null)
AND (D.TIPODOCUMENTOCLIENTE = null or null is null)
AND (D.NUMDOCUMENTOCLIENTE = null or null is null)
AND (D.BA = null or null is null)
AND (D.FA = null or null is null)
AND (D.NOMBRECLIENTE = null or null is null)
AND (D.NUMTELEFONO = null or null is null)
AND (D.NUMSUSCRIPCION = null or null is null)
AND (D.TIPORECIBO in ('Recibo'))
AND (D.NUMRECIBO = null or null is null)
AND (TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd') BETWEEN TO_DATE('2019-5-1', 'yyyy-MM-dd') AND TO_DATE('2020-2-18', 'yyyy-MM-dd'))
AND (D.MONTORECIBO = null or null is null)
AND (D.NUMPAGINAS = 0 or 0 = 0)
AND (D.NOMBREARCHIVO = null or null is null)
AND (D.NEGOCIO = null or null is null)
AND (D.NOMBREMETADATACARGA = null or null is null)
AND (D.FECHACARGA = TO_DATE(null) or TO_DATE(null) is null);
This query returns
And when I do a Xplain For:
The cost is very high, but this query uses the index. The query lasts 10 seconds approximately.
How can I improve the performance of the query?
I'm using Oracle 12c
Notes: All of the " and ( = null or null is null)" predicates will always evaluate to true; Oracle does not define null so null does not equal null, so instead if you want to check for null then use "is null"
select * from dual where null = null; -- returns no rows
select * from dual where not (null <> null); -- returns no rows
select * from dual where null is null; -- returns 1 row
select * from dual where not(null is not null); -- returns 1 row
As far as indexing goes, you need an index that is selective (i.e. return much fewer rows) and is present in the where clause predicate. In this case it looks like a function-based index on TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd')
along with D.TIPORECIBO is in order. The INDEX SKIP SCAN is used in this case probably because D.TIPORECIBO is not the leading column; INDEX SKIP SCANs are slower then INDEX RANGE SCANs because it needs to read more index blocks.
There are a few factors involved here:
First, this query is using the second (or third) part of a composite index, resulting in the SKIP SCAN.
Take a look at all indexes on the table and see what kind of index is on TIPORECIBO.
It is likely that this isn't the leading column. You might improve the performance by creating an index with TIPORECIBO as leading column, but it is unlikely--this appears to be a "type" column that might have only a few values, and not a good candidate for an index.
The second issue is that Oracle uses the index to get a set of candidate rows, then goes to the data blocks themselves to get the rows for further filtering.
A select count(*) will perform much better if Oracle doesn't need to fetch the data blocks. This can be achieved by creating an index that contains all of the data needed for the filter.
In your case, an index on TIPORECIBO and FECHAEMISION would mean that Oracle could go to the index alone without needing to access the data blocks.
The third issue is that you are applying TO_DATE to the FECHAEMISION column. If this is a DATE datatype, then you don't need the conversion and it is causing you trouble. If you do need the conversion, an option would be a function-based index on TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd').
To tune this particular query, you can try a function-based composite index:
CREATE INDEX TB_E2V_DOCUMENTOS_CICLO_FX1 ON TB_E2V_DOCUMENTOS_CICLO(FECHAEMISION, TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd'))
Finally, this query is clearly being generated from code:
lines like AND (D.BA = null or null is null) seem to be a way of excluding portions of the WHERE clause when the front-end passes a NULL. This would possibly be AND (D.BA = 'X' or 'X' is null) if a value were provided for that parameter.
As such, be careful when tuning for the current set of parameters, as any change in what generated this query will impact the effectiveness of your tuning.
If you have a way to influence how this query is generated, it would be nice to simply exclude those non-event filters when the values are not provided, though Oracle ought to be able to handle them as-is.

Randomly getting "ORA-22814: attribute or element value is larger than specified in type" with a bulk collect into

I have code that is randomly getting "ORA-22814: attribute or element value is larger than specified in type" with a bulk collect into.
I suspect this is a data issue since it's random but PL/SQL is not my strong suit but I maintain an application that has a great deal of it in processing logic.
This is an Oracle 12c database.
Any help would be appreciated!
This is the block of code:
SELECT mxpv_activityupdate_O (
projectid,
task_id,
task_code,
pvtask_code,
task_name,
CASE
WHEN wbs_t0_id IS NOT NULL AND scgemergent_flag = 'Y'
THEN
wbs_t0_id
ELSE
wbs_pv_id
END,
act_start_date,
CASE
WHEN act_end_date IS NOT NULL AND (invalidactfinish_flag = 'Y')
THEN
NULL
ELSE
act_end_date
END,
NULL,
CASE WHEN invalidactuals_flag = 'Y' THEN NULL ELSE act_work_qty END,
MAX (oid),
scgemergent_flag,
invalidactuals_flag,
invalidactfinish_flag)
BULK COLLECT INTO u_all_tab
FROM (SELECT projectorganization,
projecttype,
projectid,
task_id,
task_code,
pvtask_code,
pvact_start_date,
task_name,
act_start_date,
act_end_date,
CASE
WHEN pvact_start_date IS NULL
AND act_start_date IS NULL
AND NVL (act_work_qty, 0) <> 0
THEN
'Y'
ELSE
'N'
END
invalidactuals_flag,
act_work_qty,
oid,
wbs_t0_id,
wbs_pv_id,
CASE
WHEN pvact_start_date IS NULL
AND (act_start_date IS NOT NULL --or nvl(act_work_qty,0)<>0
)
AND projectorganization = 'SCG'
AND projecttype = 'Daily'
AND pvtarget_start_date > v_t0_start_midnight + 7
THEN
'Y'
ELSE
'N'
END
scgemergent_flag,
CASE
WHEN act_end_date IS NOT NULL
AND NVL (act_start_date, pvact_start_date) IS NOT NULL
AND act_end_date <
NVL (act_start_date, pvact_start_date)
THEN
'Y'
ELSE
'N'
END
invalidactfinish_flag
FROM (SELECT oid,
projecttype,
wbs_t0.wbs_id wbs_t0_id,
--emergentwork,
projectorganization,
projectid,
task_id,
pvwbs_id wbs_pv_id,
activityid task_code,
task_code pvtask_code,
pvact_start_date,
pvtarget_start_date,
CASE
WHEN pvact_end_date IS NULL THEN activityname
ELSE NULL
END
task_name,
CASE
WHEN actualstart IS NOT NULL
AND ( pvact_start_date IS NULL
OR actualfinish IS NOT NULL)
THEN
actualstart
ELSE
NULL
END
act_start_date,
--actualstart act_start_date,
CASE
WHEN pvact_end_date IS NULL
AND ( pvact_start_date IS NOT NULL
OR actualstart IS NOT NULL)
THEN
actualfinish
ELSE
NULL
END
act_end_date,
NULL target_start_date,
CASE WHEN mxactualunits.proj_catg_short_name = 'Y' --and nvl(pvact_work_qty, 0) <> nvl(actuallaborhours,0)
THEN NVL (actuallaborhours, 0) ELSE NULL END
act_work_qty
FROM --1) activityid and proj id matches
(SELECT oid,
pv.task_id,
mx.proj_id,
mx.projectid,
pv.task_code,
pv.task_name,
pv.wbs_id pvwbs_id,
pv.act_start_date pvact_start_date,
pv.act_end_date pvact_end_date,
pv.target_start_date pvtarget_start_date,
mx.activityid,
mx.activityname,
mx.actualstart,
mx.actualfinish,
pv.act_work_qty pvact_work_qty,
mx.actuallaborhours,
mx.projectorganization,
mx.projecttype,
mx.process_start_ts --,
FROM mxpv_activity_tmp mx, mxpv_task_tmp pv
WHERE mx.task_id = pv.task_id
UNION
--3) PM Number/Job Plan number matches
SELECT oid,
jp.task_id,
jp.proj_id,
jp.proj_short_name,
jp.task_code,
jp.task_name,
jp.wbs_id,
jp.act_start_date pvtarget_start_date,
jp.act_end_date pvtarget_end_date,
jp.target_start_date pvtarget_start_date,
jp.activityid,
jp.activityname,
jp.actualstart,
jp.actualfinish,
jp.act_work_qty pvact_work_qty,
actuallaborhours,
projectorganization,
projecttype,
p_process_start_ts --,
FROM --3a) PM Number matches
(SELECT * FROM TABLE (pm_tab)
UNION
--3a) Job Plan Number matches
SELECT * FROM TABLE (jp_tab)) jp)
update_activity,
(SELECT projpcat.PROJ_ID, pcval.PROJ_CATG_SHORT_NAME
FROM privuser_PROJPCAT projpcat,
privuser_PCATTYPE pctype,
privuser_PCATVAL pcval
WHERE projpcat.PROJ_CATG_ID = pcval.PROJ_CATG_ID
AND pcval.PROJ_CATG_TYPE_ID =
pctype.PROJ_CATG_TYPE_ID
AND pctype.PROJ_CATG_TYPE = 'MX Actual Units')
mxactualunits,
(SELECT w.wbs_id, w.proj_id
FROM privuser_projwbs w, privuser_phase ph
WHERE w.phase_id = ph.phase_id
AND ph.phase_name = '0') wbs_t0
WHERE --update_activity.proj_id = actstrtoffset.proj_id (+)
update_activity.proj_id = wbs_t0.proj_id(+)
AND update_activity.proj_id = mxactualunits.proj_id(+)
AND update_activity.process_start_ts =
p_process_start_ts--and delete_ts is null
))
GROUP BY projectid,
task_id,
task_code,
pvtask_code,
task_name,
wbs_t0_id,
wbs_pv_id,
act_start_date,
act_end_date,
act_work_qty,
invalidactuals_flag,
invalidactfinish_flag,
scgemergent_flag,
pvact_start_date;
The type is defined as follows:
TYPE PV_APP_DATA.MXPV_ACTIVITYUPDATE_O AS OBJECT(
proj_short_name VARCHAR2 (100),
task_id NUMBER,
task_code VARCHAR2 (120 CHAR),
pvtask_code VARCHAR2 (120 CHAR),
task_name VARCHAR2 (120 CHAR),
wbs_id NUMBER,
act_start_date DATE,
act_end_date DATE,
target_start_date DATE,
act_work_qty NUMBER,
oid NUMBER,
scgemergent_flag VARCHAR2 (10),
invalidactuals_flag VARCHAR2 (100),
invalidactfinish_flag VARCHAR2 (100)
)
Please post if you need more information I wasn't sure what could be useful.
update 1 7/18/17
I modified the query with substr with no luck in fixing the problem:
select mxpv_activityupdate_O(
substr(projectid,0,99),
task_id,
substr(task_code,0,119),
substr(pvtask_code,0,119),
substr(task_name,0,119),
case when wbs_t0_id is not null and scgemergent_flag='Y' then wbs_t0_id
else wbs_pv_id end,
act_start_date,
case when act_end_date is not null and
(invalidactfinish_flag='Y'
)
then null else act_end_date end,
null,
case when invalidactuals_flag = 'Y' then null else act_work_qty end,
max(oid),
substr(scgemergent_flag,0,9),
substr(invalidactuals_flag,0,99),
substr(invalidactfinish_flag,0,99)
)
bulk collect into u_all_tab
from
(SELECT
projectorganization,
projecttype,
substr(projectid,0,99) as projectid,
task_id,
substr(task_code,0,119) as task_code,
substr(pvtask_code,0,119) as pvtask_code,
pvact_start_date,
substr(task_name,0,119) as task_name,
act_start_date,
act_end_date,
case when pvact_start_date is null and act_start_date is null
and nvl(act_work_qty, 0) <> 0 then 'Y' else 'N' end invalidactuals_flag,
act_work_qty,
oid,
wbs_t0_id,
wbs_pv_id,
case when pvact_start_date is null and
(act_start_date is not null --or nvl(act_work_qty,0)<>0
)
and projectorganization='SCG' and projecttype='Daily'
and pvtarget_start_date>v_t0_start_midnight+7
then 'Y' else 'N' end scgemergent_flag,
case when act_end_date is not null and nvl(act_start_date, pvact_start_date) is not null
and act_end_date<nvl(act_start_date, pvact_start_date) then 'Y' else 'N' end
invalidactfinish_flag
FROM
"if I want to force to correct length what's the best approach?"
You need to compare the length of the Tables' columns with the length of the Type's target attributes, and see which attributes are shorter than the columns you're trying to fit into them. Use the data dictionary.
For the Type Attributes use USER_TYPE_ATTRS (or ALL_TYPE_ATTRS if it's a different schema):
select attribute_name, attr_type_name, length, scale, precision
from user_type_attrs
where type_name = 'MXPV_ACTIVITY_UPDATE_O'
/
For the Table Columns it will be a similar query based on USER_TAB_COLS.
Once you have established the errant columns you will need to alter the type to enlarge the attributes. Alternatively apply SUBSTR() function to the columns in the query projection, to cut them down to size. Which is actually better depends on the circumstances of your application, although usually we would prefer the approach which doesn't lose data.

How to create a small and simple sql without using case

This is my view sql command. I want to make it small without using case.
select a.nrc_typ_cd,a.nrc_typ_nm,a.a as "No of IMIS Source",b.b as "No of New Source",c.c as "No of Source Marked Bad",d.d as "No of Source with Lat/Long",e.e as "No of Source without Lat/Long" from
(select nrc_typ_cd,nrc_typ_nm,count(nrc_cd) as a from tableview_dtl where imis_nrc_cd is not null and act_flg='A' group by nrc_typ_cd,nrc_typ_nm ) a,
(select nrc_typ_cd,count(nvl(nrc_cd,0)) as b from tableview_dtl where imis_nrc_cd is null and act_flg='A' group by nrc_typ_cd ) b,
(select nrc_typ_cd,count(nrc_cd) as c from tableview_dtl where act_flg='I' group by nrc_typ_cd ) c,
(select nrc_typ_cd,count(nrc_cd) as d from tableview_dtl where lat_val_degree is not null and long_val_degree is not null and act_flg='A' group by nrc_typ_cd) d,
(select nrc_typ_cd,count(nvl(nrc_cd,0)) as e from tableview_dtl where lat_val_degree is null and long_val_degree is null and act_flg='A' group by nrc_typ_cd) e
where a.nrc_typ_cd=b.nrc_typ_cd
and a.nrc_typ_cd=c.nrc_typ_cd
and a.nrc_typ_cd=d.nrc_typ_cd
and a.nrc_typ_cd=e.nrc_typ_cd
order by a.nrc_typ_cd
(This is more of an extended comment than an answer.)
So this is the version of the code that you don't want?
select nrc_typ_cd,nrc_typ_nm
,count(case when imis_nrc_cd is not null and act_flg='A' then nrc_cd else null end) a
,count(case when imis_nrc_cd is not null and act_flg='A' then nvl(nrc_cd, 0) else null end) b
,count(case when act_flg='I' then nrc_cd else null end) c
,count(case when lat_val_degree is not null and long_val_degree is not null and act_flg='A' then nrc_cd else null end) d,
,count(case when lat_val_degree is null and long_val_degree is null and act_flg='A' then nvl(nrc_cd, 0) else null end) e,
from tableview_dtl
group by nrc_typ_cd,nrc_typ_nm
order by a.nrc_typ_cd;
It seems simpler, shorter, and faster than the newer version. It's important to fully explain why this obvious solution is insufficient before
people can reasonably propose alternatives.
According to a comment, this version is not acceptable because "instruction is given by my PL". (What does "PL" mean; project lead?) What does the PL have
against CASE? I can't think of a good reason to ask someone to rewrite something to avoid a CASE.
Would it be enough to use a DECODE instead? Or does the entire query need to be re-written?

Constraint check if row and other row not null on same time

I have a school 'project' to work on, which has some tables and one table needs to have a constraint which is not working out for me.
There are some tables like QUESTION, ANSWER and REACTION.
A reaction belongs with or a question or a answer but not both on the same time.
There by I have 2 rows:
question_id NUMBER,
answer_id NUMBER,
Both not null because the cant by null, but not on the same time.
I already made a constraint but isn't working..
/* CHECK if reaction belongs to an question or a answer NOT WORKING YET*/
CONSTRAINT CHECK_question_or_answer CHECK((answer_id != NULL AND question_id = NULL) OR (answer_id = NULL OR question_id != NULL))
Already tested the constraint and I can insert a value without a question_id or answer_id.
I hope it's a bit clear, if not, I am happy yo try explain myself better.
(still newby on SQL)
Thanks.
Your constraint:
CONSTRAINT CHECK_question_or_answer CHECK((answer_id != NULL AND profile_id = NULL) OR (answer_id = NULL OR profile_id != NULL))
Is always FALSE.
You need to use IS NULL or IS NOT NULL like:
CONSTRAINT CHECK_question_or_answer CHECK((answer_id IS NOT NULL AND profile_id IS NULL) OR (answer_id IS NULL OR profile_id IS NOT NULL))
This is because comparison operators != , = , > , <, combined with NULL produce NULL and are treated as false.
Demo:
SELECT 1
FROM dual
WHERE 1 IS NOT NULL;
SELECT 1
FROM dual
WHERE 1 != NULL;
From doc:
NULL values represent missing or unknown data. NULL values are used as
placeholders or as the default entry in columns to indicate that no
actual data is present. The NULL is untyped in SQL, meaning that it is
not an integer, a character, or any other specific data type.
Note that NULL is not the same as an empty data string or the
numerical value '0'. While NULL indicates the absence of a value, the
empty string and numerical zero both represent actual values.
While a NULL value can be assigned, it can not be equated with
anything, including itself.
Because NULL does not represent or equate to a data type, you cannot
test for NULL values with any comparison operators, such as =, <, or
<>.
The IS NULL and IS NOT NULL operators are used to test for NULL
values.
Do it the other way around. Put the id of the main table in the others like that
question table
--------------
id
text
...
answers table
-------------
id
question_id
text
...
reactions table
---------------
id
question_id
text
...
And question_id is never null. Then you can use a left join to get the results from both tables - one of them will have no results.
select *
from questions q
left join answers a on a.question_id = q.id
left join reactions r on r.question_id = q.id
While #lad2025s answer is good for two columns, if you wanted to extend the method to more than two it can get a bit cumbersome.
A flexible alternative is:
check ((case when answer_id is null then 0 else 1 end +
case when question_id is null then 0 else 1 end ) = 1)
It extends well to checking for a particular count of null (or non-null) values for an arbitrary number of columns.
For example, if you had column_1, column_2, column3, and column_4, and wanted at least 1 of them to be non-null, then:
check ((case when column_1 is null then 0 else 1 end +
case when column_2 is null then 0 else 1 end +
case when column_3 is null then 0 else 1 end +
case when column_4 is null then 0 else 1 end ) >= 1)

Oracle procedure 'with query' insert into table

I have created an Oracle SQL query in TOAD which works fine. I now need to put this in a procedure.
The query has to create two counts based on different criteria (I have used With Select) and insert these plus a date and location to a table.
The query that works is
with
Selected_animals as
( SELECT TO_CHAR(SYSDATE,'DD/MM/YYYY' ) as Report_Date,
loc.name location,
count(rran.id) as Count_exported
FROM rr_animals rran,
contact con,
locations loc,
names nam
WHERE con.connum = rran.connum
AND con.loc_id = loc.id
AND con.connum = nam.connum
AND nam.name_type = 'STAND'
AND nam.dob IS NOT NULL
AND rran.sex IS NOT NULL
AND rran.web_display = 'Y'
AND rran.web_description IS NOT NULL
AND rran.visit_end_date IS NULL
AND con.loc_id IS NOT NULL
AND con.datedl IS NULL
AND rran.hold_user IS NULL
AND rran.assess_status IS NULL
AND EXISTS (SELECT rrim.id
FROM rr_images rrim
WHERE rrim.image_type = 'KENNEL'
AND rrim.rran_id = rran.id
AND DBMS_LOB.GETLENGTH(rrim.image_object) >0
AND rrim.image_object IS NOT NULL)
group by loc.NAME ),
total_animals as
(select vbav.sitename as location,
count(vbav.rran_ID) as Count_available
from v_bx_all_animal_visits vbav
where visit_end_date is null
and concat != 'DELTD'
and concat != 'DSCD'
group by vbav.sitename)
select Total_animals.location,
Selected_animals.Report_date,
Selected_animals.count_exported,
Total_animals.count_available
from Selected_animals, total_animals
where total_animals.location = selected_animals.location(+)
I have looked at several ways that seem to write the procedure but nothing seems to work. Including which was added under the CREATE or REPLACE and before BEGIN:
( o_location out bx_webstats_export_available.LOCATION%TYPE,
o_date out bx_webstats_export_available.REPORT_DATE%TYPE,
o_exported out bx_webstats_export_available.COUNT_EXPORTED%TYPE,
o_available out bx_webstats_export_available.COUNT_AVAILABLE%TYPE )
Also added after the last where statement and before End:
INSERT INTO bx_webstats_export_available(location, report_date, count_export, count_available)
values (Total_animals.location,
Selected_animals.Report_date,
Selected_animals.count_exported,
Total_animals.count_available);
Can anyone help me get this query in a Procedure please?
This is the first time I have written a Procedure from scratch and I'm struggling with it.
Many thanks,
What is it you're trying to do? Insert the results of that select into a table? If so, the following ought to suffice:
create or replace procedure your_proc_name
as
begin
insert into bx_webstats_export_available(location, report_date, count_export, count_available)
with selected_animals as (select to_char(sysdate,'DD/MM/YYYY' ) as report_date,
loc.name location,
count(rran.id) as count_exported
from rr_animals rran,
contact con,
locations loc,
names nam
where con.connum = rran.connum
and con.loc_id = loc.id
and con.connum = nam.connum
and nam.name_type = 'STAND'
and nam.dob is not null
and rran.sex is not null
and rran.web_display = 'Y'
and rran.web_description is not null
and rran.visit_end_date is null
and con.loc_id is not null
and con.datedl is null
and rran.hold_user is null
and rran.assess_status is null
and exists (select rrim.id
from rr_images rrim
where rrim.image_type = 'KENNEL'
and rrim.rran_id = rran.id
and dbms_lob.getlength(rrim.image_object) >0
and rrim.image_object is not null)
group by loc.name),
total_animals as (select vbav.sitename as location,
count(vbav.rran_id) as count_available
from v_bx_all_animal_visits vbav
where visit_end_date is null
and concat != 'DELTD'
and concat != 'DSCD'
group by vbav.sitename)
select total_animals.location,
selected_animals.report_date,
selected_animals.count_exported,
total_animals.count_available
from selected_animals, total_animals
where total_animals.location = selected_animals.location(+);
end your_proc_name;
/
If not, then please explain a bit more about the requirements you're trying to satisfy.

Resources