How can i optimize this oracle query? - sql

i'm working with oracle 11g and we have a problem, well, this query takes forever to execute, the main table tbl_inc has about 17 million records, is there any way i can improve this query?
i can't add indexes, i don't have privileges for that.
SELECT count(*) FROM TBL_INC INC LEFT JOIN TBL_PDS P ON INC.SID = P.TRX
INNER JOIN TBL_ASTS ASTS ON ASTS.CODE = INC.CODE AND ASTS.MIT
= INC.MIT AND ASTS.OPE_PROD = 3
WHERE (INC.INC_DATE -1) >= to_date('29/10/20', 'DD/MM/YY')
AND INC.INC_DATE - 1 <= to_date('05/11/20', 'DD/MM/YY')
AND INC.OPE = 50 AND SUBSTR(INC.CARD_NMBR, 1, 6) = 123456
AND INC.MIT='05' AND INC.CODE='00';
thanks

First, I would rewrite this so the where clause does not have expressions on the columns:
SELECT count(*)
FROM TBL_INC INC LEFT JOIN
TBL_PDS P
ON INC.SID = P.TRX INNER JOIN
TBL_ASTS ASTS
ON ASTS.CODE = INC.CODE AND
ASTS.MIT = INC.MIT AND
ASTS.OPE_PROD = 3
WHERE INC.INC_DATE >= to_date('29/10/20', 'DD/MM/YY') + interval '1 day' AND
AND INC.INC_DATE - 1 <= to_date('05/11/20', 'DD/MM/YY') + interval '1 day' AND
INC.OPE = 50 AND
INC.CARD_NMBR LIKE '123456%' AND
INC.MIT = '05' AND INC.CODE = '00';
Then for this query, you want an index on: TBL_INC(MIT, CODE, OPE, INC_DATE, CARD_NMBR). I am guessing that you have indexes on the JOIN keys used in the other tables, but those would be TBL_PDS(TRX) and TBL_ASTS(CODE< MIT, OPE_PROD).

It is a very good practice to reduce the number of the rows from the join, rather that apply a 'where' condition to the end
SELECT count(1) FROM TBL_INC INC
INNER JOIN TBL_ASTS ASTS
ON ASTS.CODE = INC.CODE
AND ASTS.MIT = INC.MIT
AND ASTS.OPE_PROD = 3
AND INC.OPE = 50
AND SUBSTR(INC.CARD_NMBR, 1, 6) = 123456
AND INC.MIT='05'
AND INC.CODE='00'
AND (INC.INC_DATE -1) >= to_date('29/10/20', 'DD/MM/YY')
AND INC.INC_DATE - 1 <= to_date('05/11/20', 'DD/MM/YY')
LEFT JOIN
TBL_PDS P
ON INC.SID = P.TRX

Related

Optimize the Query in PostgreSQL - Takes too long

Made this view to see all the 'socios' turned their status_contrato to 301 in the last 30 days
The thing is the query takes around 30 secs, and I have to get down to 20 secs max
CREATE OR REPLACE VIEW public.socios_desligados_inadimplencis_ultimos_30_dias_v2
AS SELECT tb.nome,
tb.cpf,
tb.email,
tb.plano,
tb.status_contrato,
CASE
WHEN ad.dt_inicial IS NULL THEN tb.dt_inicial
ELSE ad.dt_inicial
END AS data_admissao,
tb.dthr_desligamento,
tb.total_parcelas_pagas
FROM ( SELECT p.idpessoa,
p.nome,
p.cpf,
p.email,
pl.dscplano AS plano,
ea.dscestado_ativacao AS status_contrato,
eal.ins_dthr AS dthr_desligamento,
cr.valor_total AS total_parcelas_pagas,
con.dt_inicial
FROM base_pessoa p
JOIN ( SELECT gs_contrato.idpessoa,
gs_contrato.idcontrato,
gs_contrato.idplano,
gs_contrato.idestado_ativacao,
gs_contrato.dt_inicial
FROM gs_contrato
WHERE gs_contrato.idestado_ativacao::text = '301'::text AND gs_contrato.alt_dthr >= (CURRENT_DATE - 30)) con ON p.idpessoa::text = con.idpessoa::text
JOIN gs_plano pl ON con.idplano::text = pl.idplano::text
JOIN gs_estado_ativacao ea ON con.idestado_ativacao::text = ea.idestado_ativacao::text
JOIN ( SELECT row_number() OVER (PARTITION BY lg.idcontrato ORDER BY lg.ins_dthr DESC) AS linha,
lg.idcontrato,
lg.idestado_ativacao,
lg.ins_dthr
FROM gs_estado_ativacao_l lg
JOIN gs_contrato con_1 ON con_1.idcontrato::text = lg.idcontrato::text
WHERE lg.idestado_ativacao::text = '301'::text AND con_1.idestado_ativacao::text = '301'::text AND con_1.alt_dthr >= (CURRENT_DATE - 30) AND lg.ins_dthr >= (CURRENT_DATE - 30)) eal ON con.idcontrato::text = eal.idcontrato::text AND eal.linha = 1
JOIN ( SELECT ccr.idcontrato,
sum(ccr.valor - ccr.valor_desconto + ccr.valor_juros + ccr.valor_multa + ccr.valor_adesao + ccr.valor_acrescimo) AS valor_total
FROM cx_conta_receber ccr
JOIN gs_contrato c ON c.idcontrato::text = ccr.idcontrato::text
WHERE ccr.status::text = 'PA'::text AND c.idestado_ativacao::text = '301'::text AND c.alt_dthr >= (CURRENT_DATE - 30)
GROUP BY ccr.idcontrato) cr ON cr.idcontrato::text = con.idcontrato::text) tb
JOIN data_adesao ad ON tb.idpessoa::text = ad.idpessoa::text;
I checked your query. I write some recommendations for performance.
Recommended using materialized CTE (with as materialized) instead of subqueries.
Maybe you have used subqueries or table-joining to get a few values. You can use return-table functions instead at this.
Check your indexes. Fields are used on the conditions (where or join), on the order by or group by commands must be indexed.
Not recommended use casting fields or set functions to fields in the conditions. In this time DB can not use indexes. If you need set functions to fields in the conditions, then recommended creating expression index (function-based index).
Your query has some cast types:
WHERE gs_contrato.idestado_ativacao::text = '301'::text
AND gs_contrato.alt_dthr >= (CURRENT_DATE - 30)) con ON
p.idpessoa::text = con.idpessoa::text
JOIN gs_plano pl
ON con.idplano::text = pl.idplano::text
JOIN gs_estado_ativacao ea
ON con.idestado_ativacao::text = ea.idestado_ativacao::text
WHERE lg.idestado_ativacao::text = '301'::text
AND con_1.idestado_ativacao::text = '301'::text
AND con_1.alt_dthr >= (CURRENT_DATE - 30)
AND lg.ins_dthr >= (CURRENT_DATE - 30)) eal ON con.idcontrato::text = eal.idcontrato::text AND eal.linha = 1
If you need cast types, so use the same cast types at creating indexes. Expression index.
And the latest and most important recommendation is that use explain analyze for this query to get execute plan of DB.

SQL Poor Performance on join query

i'm experiencing really poor performance with this query, probably because of my poor experience with join.
The following query have to run on a DB2 database.
SELECT A.n_ann_ord
|| A.n_prg_ord AS numeroOrdine,
DATE(B.d_ins) AS dataInserimentoEGO,
DATE(A.d_ord) AS dataInserimento,
C.n_lot_prd AS lotto,
E.t_rag_soc AS consorzio,
D.t_rag_soc AS cliente,
b.c_obu_new AS obuid,
j.c_tar AS targa,
j.c_naz AS nazione,
C.c_pos AS postazione,
CASE
WHEN B.f_ric_obu_sat = 'S' THEN 'Satellitare'
ELSE 'Non Satellitare'
END AS tipoTitolo,
F.t_des AS statoOrdine,
CASE
WHEN (SELECT 1
FROM evda.tetsau_sos_tit_mul X
WHERE x.d_ord = a.d_ord
FETCH first ROW only) = 1 THEN 'S'
ELSE 'N'
END AS SOSTITUZIONE,
DATE(G.d_ape_pro) AS aperturaLotto,
DATE(G.d_chiu_pro) AS chiusuraLotto,
DATE(G.d_com_con) AS confezionamento,
DATE(C.d_spe_spe) AS spedizione,
C.c_ide_spe AS trackingNumber,
DATE(H.d_acq_con) AS consegna
FROM evda.tetsvi_ord A
JOIN evda.tets3t_int_srv B
ON A.c_ctp_cli = B.c_ctp_cli
AND A.d_ord = B.d_ord
JOIN evda.tetski_int_con_ord C
ON A.c_ctp_cli = C.c_ctp_cli
AND A.d_ord = C.d_ord
JOIN evda.tets25_ctp D
ON C.c_ctp_cli = D.c_ctp
JOIN evda.tets25_ctp E
ON C.c_ctp_con = E.c_ctp
JOIN evda.tetsvk_sta_ord F
ON A.c_sta_ord = F.c_sta_ord
JOIN evda.tetsvp_pre_tit I
ON a.c_ctp_cli = i.c_ctp_cli
AND a.d_ord = i.d_ord
JOIN evda.tetsmj_tit_pre_est J
ON a.c_ctp_cli = j.c_ctp_cli
AND a.d_ord = j.d_ord
AND b.n_prg_tit_pre = j.n_prg
LEFT JOIN evda.tetskk_lot_prd G
ON C.n_lot_prd = G.n_lot_prd
LEFT JOIN evda.tets3u_int_srv_spe H
ON B.d_ins = H.d_ins
WHERE DATE(a.d_ord) <= CURRENT_DATE
AND DATE(a.d_ord) >= CURRENT_DATE - 30 DAYS
GROUP BY A.n_ann_ord
|| A.n_prg_ord,
DATE(B.d_ins),
DATE(A.d_ord),
C.n_lot_prd,
E.t_rag_soc,
D.t_rag_soc,
b.c_obu_new,
j.c_tar,
j.c_naz,
C.c_pos,
B.f_ric_obu_sat,
F.t_des,
a.d_ord,
DATE(G.d_ape_pro),
DATE(G.d_chiu_pro),
DATE(G.d_com_con),
DATE(C.d_spe_spe),
C.c_ide_spe,
DATE(H.d_acq_con)
Avoid using functions on table columns in where clause. Also, check whether "group by" is necessary as suggested by #jarlh
If d_ord is timestamp column, change where clause to
a.d_ord < timestamp( CURRENT_DATE + 1 days)
AND a.d_ord >= timestamp( CURRENT_DATE - 30 DAYS )

Combining two SQL Queries and their results

I am using Oracle database and I have written one SQL query using joins as below:
SELECT
(CT.CRDATTIM || CT.RECORDCD || CT.CRNODE) AS CALLERKEY
FROM
W67U999S F
JOIN
W03U999S CT
ON F.CCRDATTIM = CT.CRDATTIM
AND F.CRECORDCD = CT.RECORDCD
AND F.CCRNODE = CT.CRNODE
AND F.CRECORDCD = 'T'
AND CT.WRKTYPE = 'CALLER'
JOIN
PQCUSTSRV CDT
ON F.CCRDATTIM = CDT.CRDATTIM
AND F.CRECORDCD = CDT.RECORDCD
AND F.CCRNODE = CDT.CRNODE
WHERE
F.PCRDATTIM = '2019-01-29-06.10.33.037940'
AND F.PRECORDCD = 'F'
AND F.PCRNODE = '01'
AND CT.CRDATTIM < '9999-12-31-23.59.59.999999'
The above query returns me the following rows of data:
CALLERKEY
----------
2019-01-29-06.10.33.310940T01
2019-03-26-00.51.05.124040T01
2019-03-26-00.51.07.993040T01
2019-03-26-00.51.35.531040T01
2019-04-02-04.02.42.280040T01
Now I want to query for each row using the following SQL query to get some additional data that I am interested in:
Example as below:
SELECT COUNT(*) AS TESTCOUNT FROM PQCUSTSRV where CALLERKEY='2019-01-29-06.10.33.310940T01' AND TO_TIMESTAMP(CRDATTIM,'YYYY-MM-DD HH24.MI.SS.FF')
BETWEEN ADD_MONTHS(SYSTIMESTAMP,-12) AND SYSTIMESTAMP
The above query returns the following data:
TESTCOUNT
------------
2
Now I want to combine the above two queries and get the output as something like below:
CALLERKEY TESTCOUNT
---------- --------
2019-01-29-06.10.33.310940T01 2
2019-03-26-00.51.05.124040T01 3
2019-03-26-00.51.07.993040T01 2
2019-03-26-00.51.35.531040T01 1
2019-04-02-04.02.42.280040T01 4
How can I achieve that? Please let me know if you need any additional information.
with t as (
<first query here>
)
select t.callerkey, count(p.callerkey) AS TESTCOUNT
from t left join
PQCUSTSRV p
on p.callerkey = t.callerkey and
to_timestamp(p.CRDATTIM, 'YYYY-MM-DD HH24.MI.SS.FF') >= add_months(SYSTIMESTAMP, -12)
group by t.callerkey;
I changed the logic of the date comparison to just use >=, assuming that you don't have future values in crdattim.
You can use group by as follows:
Select F.CALLERKEY, COUNT(1) AS CNT
From (your_first_query) f
Left Join PQCUSTSRV s
On s.CALLERKEY=f.CALLERKEY AND TO_TIMESTAMP(CRDATTIM,'YYYY-MM-DD HH24.MI.SS.FF')
BETWEEN ADD_MONTHS(SYSTIMESTAMP,-12) AND SYSTIMESTAMP
Group by f.callerkey
Not tested but this should work
SELECT A.CALLERKEY, COUNT(*) AS TESTCOUNT
(SELECT (CT.CRDATTIM || CT.RECORDCD || CT.CRNODE) AS CALLERKEY
FROM W67U999S F
JOIN W03U999S CT
ON F.CCRDATTIM = CT.CRDATTIM
AND F.CRECORDCD = CT.RECORDCD
AND F.CCRNODE = CT.CRNODE
AND F.CRECORDCD = 'T'
AND CT.WRKTYPE = 'CALLER'
JOIN PQCUSTSRV CDT
ON F.CCRDATTIM = CDT.CRDATTIM
AND F.CRECORDCD = CDT.RECORDCD
AND F.CCRNODE = CDT.CRNODE
WHERE F.PCRDATTIM = '2019-01-29-06.10.33.037940'
AND F.PRECORDCD = 'F'
AND F.PCRNODE = '01'
AND CT.CRDATTIM < '9999-12-31-23.59.59.999999')A, PQCUSTSRV B
WHERE A.CALLERKEY=B.CALLERKEY AND TO_TIMESTAMP(CRDATTIM,'YYYY-MM-DD HH24.MI.SS.FF')
BETWEEN ADD_MONTHS(SYSTIMESTAMP,-12) AND SYSTIMESTAMP
GROUP BY B.CALLERKEY

where clause, case statement

I need to have the case statement shown below added to the main query so that it excludes members when different dates. How can I add it in? Thanks.
SELECT DISTINCT
PI.INDEX_VALUE,
LB.STATE,
UNIQUE_ID,
'AL',
'EN',
MBP.START_DATE,
ADD_MONTHS(MBP.START_DATE,12)
FROM dwteam.PATIENT_DETAILS PD
INNER JOIN dwteam.ADMIN_CLIENT AC
ON AC.CLIENT_ID = PD.CLIENT_ID
and AC.DELETED_BY is null
-- and AC.IS_ACTIVE =1
INNER JOIN dwteam.MEM_BENF_PROG MBP
ON MBP.MEMBER_ID = PD.PATIENT_ID
and MBP.DELETED_BY is null
INNER JOIN dwteam.BENF_PLAN_PROG BPP
ON BPP.BEN_PLAN_PROG_ID = MBP.BEN_PLAN_PROG_ID
INNER JOIN dwteam.BENEFIT_PROGRAM BPR
ON BPR.BENEFIT_PROGRAM_ID = BPP.BENEFIT_PROGRAM_ID
and BPR.DELETED_BY is null
left JOIN dwteam.PATIENT_INDEX PI
ON PI.PATIENT_ID=MBP.MEMBER_ID
AND PI.INDEX_ID in (10073,10071)
left join UHG_000613129.HFS_LOB_ID_XWALK LB
on trim(upper(replace(replace(AC.CLIENT_NAME,'C&S',''),'M&R','')))=trim(upper(STATE_DESC))
WHERE PROGRAM_NAME IN ('Healthy Pregnancy','High Risk Pregnancy')
AND (sysdate < ADD_MONTHS(MBP.START_DATE,12)
OR sysdate < ADD_MONTHS(MBP.END_DATE,12))
CASE WHEN MBP.CLOSURE_ID = 40 THEN MBP.END_DATE - 120
WHEN MBP.CLOSURE_ID IN (5,18,103) THEN MBP.END_DATE - 365
ELSE MBP.END_DATE - 270 END AS END_DATE
You can use a CTE to name an expression, and then use it for any purpose you want in the outer query.
I don't know all the details of your query but it should take the following form:
with
x as (
SELECT DISTINCT
PI.INDEX_VALUE,
LB.STATE,
UNIQUE_ID,
'AL',
'EN',
MBP.START_DATE,
ADD_MONTHS(MBP.START_DATE,12) as m1,
CASE WHEN MBP.CLOSURE_ID = 40 THEN MBP.END_DATE - 120
WHEN MBP.CLOSURE_ID IN (5,18,103) THEN MBP.END_DATE - 365
ELSE MBP.END_DATE - 270 END AS END_DATE1
FROM dwteam.PATIENT_DETAILS PD
JOIN dwteam.ADMIN_CLIENT AC ON AC.CLIENT_ID = PD.CLIENT_ID
AND AC.DELETED_BY is null
JOIN dwteam.MEM_BENF_PROG MBP ON MBP.MEMBER_ID = PD.PATIENT_ID
AND MBP.DELETED_BY is null
JOIN dwteam.BENF_PLAN_PROG BPP ON BPP.BEN_PLAN_PROG_ID
= MBP.BEN_PLAN_PROG_ID
JOIN dwteam.BENEFIT_PROGRAM BPR ON BPR.BENEFIT_PROGRAM_ID
= BPP.BENEFIT_PROGRAM_ID
AND BPR.DELETED_BY is null
left JOIN dwteam.PATIENT_INDEX PI ON PI.PATIENT_ID=MBP.MEMBER_ID
AND PI.INDEX_ID in (10073,10071)
left join UHG_000613129.HFS_LOB_ID_XWALK LB
on trim(upper(replace(replace(AC.CLIENT_NAME,'C&S',''),'M&R','')))
= trim(upper(STATE_DESC))
WHERE PROGRAM_NAME IN ('Healthy Pregnancy','High Risk Pregnancy')
AND (sysdate < ADD_MONTHS(MBP.START_DATE,12)
OR sysdate < ADD_MONTHS(MBP.END_DATE,12))
)
SELECT *
FROM x
WHERE end_date1 > ... -- filtering predicate(s) using a named expression
Tweak as necessary.

SQL Show dates without transactions as value = 0

I'm doing a weight reporting and I have a problem. I use this query to know the enters of weight in our warehouse, but when there are no transactions in a date this date doesn't appears in the results.
SELECT erp.MKPF.BUDAT AS Data,
Sum( erp.MSEG.MENGE * erp.MARM.BRGEW ) as pes
From erp.MKPF
INNER Join erp.MSEG on erp.MKPF.MANDT = erp.MSEG.MANDT and erp.MKPF.MBLNR = erp.MSEG.MBLNR
INNER Join erp.MARM on erp.MSEG.MANDT = erp.MARM.MANDT and erp.MSEG.MATNR = erp.MARM.MATNR And erp.MSEG.MEINS = erp.MARM.MEINH
INNER JOIN erp.MARA on erp.MSEG.MANDT = erp.MARA.MANDT and erp.MSEG.MATNR = erp.MARA.MATNR
WHERE erp.MKPF.MANDT = '100'
and erp.MKPF.BUDAT >= '20120720'
and erp.MKPF.BUDAT <= CONVERT(VARCHAR(8), GETDATE(), 112) -1
and erp.MSEG.LGORT in ('1001','1069')
and erp.MSEG.BWART In ('101','102','311','312')
and erp.MSEG.WERKS = '1001'
and erp.MARA.MTART in ('Z001','Z010','Z002','Z02E')
GROUP BY erp.MKPF.BUDAT*
Now the results are like this:
Data PES
20120720 9999999.9999
20120721 9999999.8888
20120723 9999999.7777
And i need this
Data PES
20120720 9999999.9999
20120721 9999999.8888
20120722 0
20120723 999999.7777
Can somebody help me?
Use a table or a view to generate the date range of interest and let this drive the query. Then you outer join your results to this view. This can be done dynamically in the query. For example, in Oracle, you can use "connect by" to generate a series:
create table my_summary(the_day date, pes number);
insert into my_summary values(to_date('20120720', 'yyyymmdd'), 9999999.9999);
insert into my_summary values(to_date('20120721', 'yyyymmdd'), 9999999.8888);
insert into my_summary values(to_date('20120723', 'yyyymmdd'), 9999999.7777);
SELECT d.the_day, NVL(s.pes, 0) AS pes
FROM ( SELECT to_date('20120720', 'yyyymmdd') + level -1 AS the_day
FROM dual CONNECT BY level <= 4) d
LEFT OUTER JOIN my_summary s ON (d.the_day = s.the_day)
ORDER BY 1
THE_DAY PES
--------- ---
20-JUL-12 9999999.9999
21-JUL-12 9999999.8888
22-JUL-12 0
23-JUL-12 9999999.7777
Other rdbms have other methods to generate a series. This will require you to know the start date you want, and the number of records (in the example above 20120720 and 4).
Thanks to all, finally I did this and it works
SELECT
c.BUDAT AS DATA,
CASE When SAP.pes Is Null then '0'
ELSE SAP.pes
END
From
erp.YSD_CALENDAR as c LEFT JOIN
(SELECT
erp.MKPF.BUDAT,
Sum(
erp.MSEG.MENGE
* erp.MARM.BRGEW ) as pes
FROM
erp.MKPF
INNER Join erp.MSEG on erp.MKPF.MANDT = erp.MSEG.MANDT and erp.MKPF.MBLNR = erp.MSEG.MBLNR
INNER Join erp.MARM on erp.MSEG.MANDT = erp.MARM.MANDT and erp.MSEG.MATNR = erp.MARM.MATNR And erp.MSEG.MEINS = erp.MARM.MEINH
INNER JOIN erp.MARA on erp.MSEG.MANDT = erp.MARA.MANDT and erp.MSEG.MATNR = erp.MARA.MATNR
WHERE
erp.MKPF.MANDT = '100'
and erp.MKPF.BUDAT >= '20120720'
and erp.MSEG.LGORT in ('1001','1069')
and erp.MSEG.BWART In ('101','102','311','312')
and erp.MSEG.WERKS = '1001'
and erp.MARA.MTART in ('Z001','Z010','Z002','Z02E')
and erp.MSEG.SHKZG = 'S'
GROUP BY erp.MKPF.BUDAT
) SAP ON SAP.BUDAT = c.BUDAT
WHERE
c.BUDAT >= '20120720'
and c.BUDAT <= CONVERT(VARCHAR(8), GETDATE(), 112)
GROUP BY c.BUDAT, SAP.pes
ORDER BY c.BUDAT