Query optimization for multiple subsets - sql

I have a "personnel basic information table", and it will be referenced by many "personnel subset tables" (there is no relationship between these subset tables, they are just a subset of the basic information of the personnel).
Now there is a requirement to merge the "personnel subset" into a column based on the person's foreign key and display it as a field of the "personnel basic information" (after the "personnel subset" is merged, each person record will only display one row) .
And the basic personnel information and personnel subset information will be passed into the query conditions, so these conditions are dynamically assembled.
It has been implemented now, but the problem is that the query needs to be optimized. Execution will be particularly slow once.
Database is oracle 11g.
Here is the sql generated by a certain query:
WITH in_hr_work_party_current AS (
SELECT
*
FROM
hr_work_party
WHERE
status = '1'
AND removaltime IS NULL
AND (
appointorg IS NOT NULL
OR NAME IS NOT NULL
)
),
qv_hr_work_party_current AS (
SELECT
empid,
listagg (
DECODE (
appointorg, NULL, '', 'oidstart' || appointorg || 'oidend-'
) || NVL (NAME, 'xx'),
','
) WITHIN GROUP (
ORDER BY
positiongrade ASC,
positionorder ASC,
appointtime ASC
) AS party_current_info
FROM
in_hr_work_party_current
GROUP BY
empid
),
in_position_postwork_main AS (
SELECT
po.empid,
working_coid_name AS post_main_coid_name,
working_poid_name AS post_main_poid_name,
officedepname AS post_main_officedepname,
post AS post_main_post,
posttype AS post_main_posttype,
workprof AS post_main_workprof,
starttime AS post_main_starttime,
FLOOR (
CEIL (
MONTHS_BETWEEN (
NVL (
TO_DATE (po.endtime, 'yyyy-MM-dd'),
SYSDATE
),
TO_DATE (po.starttime, 'yyyy-MM-dd')
)
) / 12
) || 'xx' AS post_main_years,
epo.entrytime AS post_main_entrytime,
ROW_NUMBER () OVER (
PARTITION BY po.empid
ORDER BY
po.opttime DESC
) AS rn
FROM
hr_work_postworkinfo po,
hr_work_empposition epo,
v_waf_ac_organ_base dep
WHERE
DEP.OID = PO.officedepid
AND epo.wepid = po.wepid
AND po.status = '1'
AND po.officetype = '00'
AND is_date (po.starttime) = 1
AND (
is_date (po.endtime) = 1
OR po.endtime IS NULL
)
AND epo.empsort = '01'
AND epo.subrelation = '01'
AND dep.orule LIKE '-xxxxx-%'
),
qv_position_postwork_main AS (
SELECT
*
FROM
in_position_postwork_main
WHERE
rn = 1
),
in_postworkinfo AS (
SELECT
po.*,
epo.entrytime,
epo.leavetime,
epo.empsort,
epo.empstatus,
epo.subrelation,
(
working_coid_name || DECODE (
working_poid_name, NULL, '', '-' || working_poid_name
) || DECODE (
officedepname, NULL, '', '-' || officedepname
)
) orgname,
FLOOR (
CEIL (
MONTHS_BETWEEN (
NVL (
TO_DATE (po.endtime, 'yyyy-MM-dd'),
SYSDATE
),
TO_DATE (po.starttime, 'yyyy-MM-dd')
)
) / 12
) postyear,
MOD (
CEIL (
MONTHS_BETWEEN (
NVL (
TO_DATE (po.endtime, 'yyyy-MM-dd'),
SYSDATE
),
TO_DATE (po.starttime, 'yyyy-MM-dd')
)
),
12
) postmonth,
ROW_NUMBER () OVER (
PARTITION BY po.empid
ORDER BY
po.starttime DESC
) rn,
COUNT (*) OVER (PARTITION BY po.empid) ct
FROM
hr_work_postworkinfo po,
HR_WORK_EMPPOSITION EPO,
V_WAF_AC_ORGAN_BASE DEP
WHERE
po.wepid = EPO.wepid
AND PO.officedepid = DEP.OID
AND is_date (starttime) = 1
AND (
is_date (endtime) = 1
OR endtime IS NULL
)
AND epo.empstatus != '05'
AND DEP.orule LIKE '-xxxxxx-%'
),
qv_hr_position_postwork AS (
SELECT
empid,
listagg (
'[' || starttime || 'xx' || NVL (endtime, 'xx') || 'xx' || orgname || 'xx' || NVL (post, 'xx') || 'xx' || NVL (posttype, 'xx') || 'xx' || DECODE (
postyear, 0, '', postyear || 'xx'
) || DECODE (
postmonth, 0, '', postmonth || 'xxxx'
) || 'xx' || NVL (postgradation, 'xx') || 'xxxx' || entrytime || 'xx' || empsort || 'xx' || empstatus || 'xx' || subrelation || ']',
','
) WITHIN GROUP (
ORDER BY
starttime DESC
) || (
CASE WHEN ct >= 5 THEN ',...' ELSE '' END
) work_info
FROM
in_postworkinfo
WHERE
rn <= 5
GROUP BY
empid,
ct
),
in_hr_emp_basicinfo AS (
SELECT
emp.*
FROM
hr_emp_basicinfo emp
),
qv_hr_emp_basicinfo_view AS (
SELECT
in_hr_emp_basicinfo.*
FROM
in_hr_emp_basicinfo
),
in_party AS (
SELECT
hr_work_party.NAME,
hr_work_party.empid,
hr_work_party.appointno,
hr_work_party.appointtime,
hr_work_party.removaltime,
hr_work_party.positiongrade,
NVL (
waf_ac_organ.NAME, hr_work_party.appointorg
) apporg,
ROW_NUMBER () OVER (
PARTITION BY hr_work_party.empid
ORDER BY
appointtime DESC,
removaltime DESC,
positiongrade ASC,
positionorder ASC
) rn,
COUNT (*) OVER (PARTITION BY hr_work_party.empid) ct
FROM
hr_work_party,
waf_ac_organ
WHERE
hr_work_party.status = '1'
AND hr_work_party.appointorg = waf_ac_organ.OID (+)
AND (
hr_work_party.appointorg IS NOT NULL
OR hr_work_party.NAME IS NOT NULL
)
),
qv_hr_work_party AS (
SELECT
empid,
listagg (
'[' || appointtime || 'xx' || NVL (removaltime, 'xx') || ' ' || apporg || '-' || NVL (NAME, 'xx') || DECODE (
NVL (appointno, 'xx'),
'xx',
'',
' - ' || appointno
) || ']',
','
) WITHIN GROUP (
ORDER BY
rn ASC
) || (
CASE WHEN ct > 10 THEN ',...' ELSE '' END
) app_party,
listagg (
'[' || appointtime || 'xx' || NVL (removaltime, 'xx') || ' ' || apporg || '-' || NVL (NAME, 'xx') || DECODE (
positiongrade, NULL, '', '(' || positiongrade || ')'
) || ']',
','
) WITHIN GROUP (
ORDER BY
rn ASC
) || (
CASE WHEN ct > 10 THEN ',...' ELSE '' END
) AS party_grade
FROM
in_party
WHERE
rn <= 10
GROUP BY
empid,
ct
)
SELECT
*
FROM
(
SELECT
dumb_table.*,
ROWNUM dumb_rn
FROM
(
SELECT
qv_hr_work_party_current.party_current_info,
qv_position_postwork_main.post_main_coid_name,
qv_position_postwork_main.post_main_poid_name,
qv_position_postwork_main.post_main_officedepname,
qv_position_postwork_main.post_main_years,
qv_hr_emp_basicinfo_view.age,
qv_hr_emp_basicinfo_view.NAME,
qv_hr_position_postwork.work_info
FROM
qv_hr_work_party_current,
qv_position_postwork_main,
qv_hr_work_party,
qv_hr_position_postwork,
qv_hr_emp_basicinfo_view
WHERE
1 = 1
AND qv_hr_emp_basicinfo_view.empid = qv_hr_work_party_current.empid(+)
AND qv_hr_emp_basicinfo_view.empid = qv_position_postwork_main.empid(+)
AND qv_hr_emp_basicinfo_view.empid = qv_hr_work_party.empid (+)
AND qv_hr_emp_basicinfo_view.empid = qv_hr_position_postwork.empid
AND qv_hr_emp_basicinfo_view.empid IS NOT NULL
ORDER BY
qv_hr_emp_basicinfo_view.sno
) dumb_table
)
WHERE
dumb_rn <= 30
AND dumb_rn > 0
enter image description here

Related

How to remove WITH clauses Oracle query

I have a query where I have to remove the WITH clauses and it still have to return the same results. One of the clauses should go in to Join I guess and the other one in having ? Am I in a right direction...
Any suggestion
with QA_m as(
select adr_id, max(eff_ts)as EFF_TS
from addr group by adr_id)
,
QA_address as(select q.adr_id,q.EFF_TS,d.COUNTRY_ID,d.city,d.POST_CODE,d.STREET,d.UNIT_NBR,d.ADL_INFO
from QA_m q join ADDR d
on q.adr_id=d.adr_id and q.EFF_TS=d.EFF_TS)
select
c.SRGT_KEY_VAL as Customer_id,
CAST(i.EFF_TS as date) as "EFF_DT",
i.EFF_TS,
'9999-12-31' as END_DT,
'N' as DEL_IND,
'I' as CUSTOMER_TYPE,
case when ctr.NAME = 'Canada' then 'Y'
else 'N'
END as RESIDENCE_FLAG,
NVL(ctr.name,'N/A') as country,
NVL((trim(TITLE) ||' '||trim(FIRST_NAME)||' '||trim(LAST_NAME)),' ') as NAME,
NVL((trim(CITY)||' '||trim(POST_CODE)||' '||trim(STREET)||' '||trim(UNIT_NBR)||' '||trim(ADL_INFO)),' ') as ADDRESS,
case when i.BIRTH_DATE=TO_date('9999-12-31') then NULL
else TRUNC(months_between(sysdate, i.BIRTH_DATE) / 12)
end AGE,
case when substr(GENDER,1,1) = 'M' then 'M'
when substr(GENDER,1,1) = 'm' then 'M'
when substr(GENDER,1,1) = 'F' then 'F'
when substr(GENDER,1,1) = 'f' then 'F'
else NULL
end as GENDER,
NULL as VAT_NUMBER,
NULL as BRANCH,
NULL as EMPLOYEES
from IDV i
join CSTMR_SRGT_KEY c
on i.IDV_ID=c.ntrl_key_val
left join QA_address B
on i.adr_ID=b.adr_id
left join COUNTRY ctr
on ctr.COUNTRY_ID=b.COUNTRY_ID
where SRC_STM_ID = 100
and i.END_TS='9999-12-31 23:59:59.999999000'
and i.DEL_IND='N';
These are subqueries, so - just put them into appropriate places (see comments which indicate that):
SELECT c.srgt_key_val AS customer_id,
CAST (i.eff_ts AS DATE) AS "EFF_DT",
i.eff_ts,
'9999-12-31' AS end_dt,
'N' AS del_ind,
'I' AS customer_type,
CASE WHEN ctr.name = 'Canada' THEN 'Y' ELSE 'N' END AS residence_flag,
NVL (ctr.name, 'N/A') AS country,
NVL (
( TRIM (title)
|| ' '
|| TRIM (first_name)
|| ' '
|| TRIM (last_name)),
' ') AS name,
NVL (
( TRIM (city)
|| ' '
|| TRIM (post_code)
|| ' '
|| TRIM (street)
|| ' '
|| TRIM (unit_nbr)
|| ' '
|| TRIM (adl_info)),
' ') AS address,
CASE
WHEN i.birth_date = TO_DATE ('9999-12-31') THEN NULL
ELSE TRUNC (MONTHS_BETWEEN (SYSDATE, i.birth_date) / 12)
END age,
CASE
WHEN SUBSTR (gender, 1, 1) = 'M' THEN 'M'
WHEN SUBSTR (gender, 1, 1) = 'm' THEN 'M'
WHEN SUBSTR (gender, 1, 1) = 'F' THEN 'F'
WHEN SUBSTR (gender, 1, 1) = 'f' THEN 'F'
ELSE NULL
END AS gender,
NULL AS vat_number,
NULL AS branch,
NULL AS employees
FROM idv i
JOIN cstmr_srgt_key c ON i.idv_id = c.ntrl_key_val
LEFT JOIN ( --> this is QA_address
SELECT q.adr_id,
q.eff_ts,
d.country_id,
d.city,
d.post_code,
d.street,
d.unit_nbr,
d.adl_info
FROM ( --> this is QA_m
SELECT adr_id, MAX (eff_ts) AS eff_ts
FROM addr
GROUP BY adr_id) q
JOIN addr d
ON q.adr_id = d.adr_id
AND q.eff_ts = d.eff_ts) b
ON i.adr_id = b.adr_id
LEFT JOIN country ctr ON ctr.country_id = b.country_id
WHERE src_stm_id = 100
AND i.end_ts = '9999-12-31 23:59:59.999999000'
AND i.del_ind = 'N';

Pivoting AWS Redshift SUPER data

I have a following table structure:
server_id
server_databases
1
[{"name": "mssql", "count": 12},{"name": "postgresql", "count": 1}]
2
[]
3
null
What I want to receive as a result(I want to keep servers 2 and 3 if it would be null or empty object doesn't matter):
server_id
databases
1
{"mssql": 12, "postgresql": 1}
2
null
3
null
I've tried to build json myself
SELECT server_id,
(
select '{' || listagg('"' || x.name || '":' || x.count, ',') || '}' as clientdatabases
from (
select cb."name"::varchar as name, sum(cb."count")::int as count from e.server_databases as cb group by name
) x
)
FROM my_table e
But it fails with interestiong error
[XX000] ERROR: Query unsupported due to an internal error. Detail: Unsupported witness case Where: nested_decorrelate_calc_witness_unsupported|calc_witness
It looks like PartiQL supports such cases, but I have no idea how to implement it. I will use UDF for now. But, if you can help me with a "native" solution, it would be amazing.
Update SQL script for case reproduction:
CREATE table my_table(server_id int, server_databases super);
insert into my_table(server_id, server_databases) values (
1, json_parse('[{"name": "mssql", "count": 12},{"name": "postgresql", "count": 1}]')
),
(2, json_parse('[]')),
(3, null);
SELECT server_id,
(
select '{' || listagg('"' || x.name || '":' || x.count, ',') || '}' as clientdatabases
from (
select cb."name"::varchar as name, sum(cb."count")::int as count from e.server_databases as cb group by name
) x
)
FROM my_table e;
Use ISNULL in count :
SELECT server_id,
(
select '{' || listagg('"' || x.name || '":' || x.count, ',') || '}' as clientdatabases
from (
select cb."name"::varchar as name, ISNULL(sum(cb."count")::int,0) as count from e.server_databases as cb group by name
) x
)
FROM my_table e
You can use left join for handling null cases
with b as (select server_id,
('{' || listagg('"' || x.name::text || '":' || x.count::int, ',') || '}') as clientdatabases
from my_table as e, e.server_databases as x
group by server_id)
select a.server_id, a.server_databases, json_parse(b.clientdatabases)
from my_table as a
left join b
on a.server_id = b.server_id;

Pivot BigQuery table using multiple rows

In order to pivot my big query table, I found this code
SELECT 'SELECT id, ' ||
STRING_AGG(
'MAX(IF(key = "' || key || '", value, NULL)) as `' || key || '`'
)
|| ' FROM `project.dataset.table` GROUP BY id ORDER BY id'
FROM (
SELECT key
FROM `project.dataset.table`
GROUP BY key
ORDER BY key
But even if I apply EXECUTE IMMEDIATE function, it returns a string of the code above.
What did I missed in that function ?
Thanks for your help
Use below
EXECUTE IMMEDIATE(
SELECT 'SELECT id, ' ||
STRING_AGG(
'MAX(IF(key = "' || key || '", value, NULL)) as `' || key || '`'
)
|| ' FROM `project.dataset.table` GROUP BY id ORDER BY id'
FROM (
SELECT key
FROM `project.dataset.table`
GROUP BY key
ORDER BY key
)
);

SQL optimization with indexes

I have been trying hard to optimize below query using indexes in place of where clause. can any one help on the same. Data volume is very high so I want to optimize it using indexing or anyother way?
Query goes like this:
SELECT *
FROM
(SELECT
a.*,
rownum as row_num
FROM
(SELECT DISTINCT
lot.lot_no AS lotNo, lot.sel as sel,
lot.C_ARRIVAL_NOTICE_NO AS arrNoticeNo,
lot.C_SHIPMENT_DIRECTION AS shipmentDirection,
lot.C_SENDERS_REFERENCE_NUM AS externalReference,
lot.booking_seq AS bookingNo,lot.shipper AS shipperCode,
nvl(lot.shipper_name ,lot.shipper_addr1) AS shipperName,
NVL2 (lot.commdesc, lot.commdesc || ' ', '') || NVL2 (lot.comdesc1,lot. comdesc1 || ' ', '') || NVL2 (lot.comdesc2, lot.comdesc2 || ' ', '') || NVL2 (lot.comdesc3, lot.comdesc3 || ' ', '') || NVL2 (lot.comdesc4, lot.comdesc4 || ' ', '') || NVL2 (lot.comdesc5, lot.comdesc5 || ' ', '') || NVL2 (lot.comdesc6,lot. comdesc6 || ' ', '') || NVL2 (lot.comdesc7,lot. comdesc7 || ' ', '') || NVL2 (lot.comdesc8, lot.comdesc8 || ' ', '') || NVL2 (lot.comdesc9, lot.comdesc9, '') AS description,
lot.lot_qty AS pieces, lot.lot_wght AS weight,
lot.lot_cube AS cube,
to_char(lot.input_date, 'dd-Mon-YYYY') AS lotDate,
lot.e_m AS e_m, lot.warehouse AS warehouse,
to_char(lot.in_date , 'dd-Mon-YYYY') AS inDate,
lot.in_time AS inTime, lot.hold AS hold,
to_char(lot.sail_date, 'dd-Mon-YYYY') AS sailDate,
to_char(lot.sail_date, 'dd-Mon-YYYY') AS etdDate,
lot.container AS container,
lot.oml_bl_no AS billOfLadingNumber,
lot.reference AS fileNumber,
lot.inland_carrier AS trucker,
lot.pro_number AS proNumber,
lot.comments AS exceptions, lot.vessel AS vessel,
lot.cstatus AS status, lot.voyage AS voyage,
(SELECT count(*) FROM tra_shipment_status
WHERE c_reference = lot.lot_no AND i_status_code = '857940') as transmitcount,
(SELECT c_finalcfs_code FROM imp_bl_top
WHERE inv_no = lot.C_Arrival_Notice_No) as cfsCode,
'STI_COUNTRY_US' AS schemaName
FROM
itemdata lot
WHERE
lot.in_date BETWEEN TO_DATE('27-FEB-2017 00:00', 'DD-MON-YYYY HH24:MI')
AND TO_DATE('29-MAR-2017 23:59', 'DD-MON-YYYY HH24:MI')
AND lot.sel IS NOT NULL
AND (lot.C_ARRIVAL_NOTICE_NO IS NOT NULL OR
lot.C_SHIPMENT_DIRECTION ='IO')
AND lot.I_STATUS > 0 AND Lot.Cstatus = 'D'
ORDER BY
lot.lot_no desc, lot.in_date desc) a
WHERE
rownum <= 60)
WHERE
row_num >= 1

How to sort data in Oracle SQL with sub query and wm_concat

Below is a sub-query of a bigger query, what I am trying to do is to get last 5 documents sorted by SL_DT in descending.
I always get an error that the right parenthesis is missing, I have also considered using row_number() over (order by pa.last_modified_date desc) but it doesn't work.
SELECT REPLACE (
wm_concat( SL_TXN_CODE
|| ' - '
|| SL_NO
|| '('
|| SL_DT
|| ') - '
|| SUM (SL_QTY)),
',',
' ,'
)
FROM STK_LEDGER
WHERE ROWNUM <= 5
AND SL_ITEM_CODE =
(SELECT IDH_ITEM_CODE
FROM AA_ITEM_DEFINATION_HEAD
WHERE IDH_SUPP_BC_1 = '111' OR IDH_ITEM_CODE = '111')
AND SL_TXN_TYPE IN ('SARTN', 'GRN', 'LTRFI')
AND SL_LOCN_CODE NOT IN ('D2', 'D4', 'D5')
GROUP BY SL_TXN_CODE, SL_NO, SL_DT
ORDER BY SL_DT DESC
Please suggest the best way to sort SL_DT in descending and getting the 5 records only. As you can see that I need all data in one single field.
The database is Oracle 10g.
Thanks in advance.
SELECT VALUE
FROM (SELECT VALUE, ROWNUM AS ROW_NUM
FROM (SELECT REPLACE(WM_CONCAT(SL_TXN_CODE || ' - ' || SL_NO || '(' ||
SL_DT || ') - ' || SUM(SL_QTY)),
',',
' ,') AS VALUE
FROM STK_LEDGER
WHERE SL_ITEM_CODE =
(SELECT IDH_ITEM_CODE
FROM AA_ITEM_DEFINATION_HEAD
WHERE IDH_SUPP_BC_1 = '111'
OR IDH_ITEM_CODE = '111')
AND SL_TXN_TYPE IN ('SARTN', 'GRN', 'LTRFI')
AND SL_LOCN_CODE NOT IN ('D2', 'D4', 'D5')
GROUP BY SL_TXN_CODE, SL_NO, SL_DT
ORDER BY SL_DT DESC))
WHERE ROW_NUM <= 5