Oracle mutliple left outer joins only max() rows - oracle

I have a statement that is used quiet often but does perform pretty poorly.
So i want to optimize it as good as possible. The statement consist of various parts and unions.
One part that is pretty costly is the following.
select *
from rc050 F
LEFT OUTER JOIN
(SELECT fi_nr,
fklz,
rvc_status,
lfdnr,
txt
FROM rc0531 temp
WHERE lfdnr =
(SELECT MAX(lfdnr) FROM rc0531 WHERE fi_nr = temp.fi_nr AND fklz = temp.fklz
)
) SUB_TABLE1
ON F.fklz = SUB_TABLE1.fklz
AND F.fi_nr = SUB_TABLE1.fi_nr
LEFT OUTER JOIN
(SELECT fi_nr,
fklz,
rvc_status,
lfdnr,
txt
FROM rc0532 temp
WHERE lfdnr =
(SELECT MAX(lfdnr) FROM rc0532 WHERE fi_nr = temp.fi_nr AND fklz = temp.fklz
)
) SUB_TABLE2
ON F.fklz = SUB_TABLE2.fklz
AND F.fi_nr = SUB_TABLE2.fi_nr
LEFT OUTER JOIN
(SELECT fi_nr,
fklz,
rvc_status,
lfdnr,
txt
FROM rc05311 temp
WHERE lfdnr =
(SELECT MAX(lfdnr)
FROM rc05311
WHERE fi_nr = temp.fi_nr
AND fklz = temp.fklz
)
) SUB_TABLE11
ON F.fklz = SUB_TABLE11.fklz
AND F.fi_nr = SUB_TABLE11.fi_nr
where F.fklz != ' '
This is a part where I have to load from these rc0531 ... rc05311 tables
what there latest entry is. There are 11 of these tables so this is broken down.
As you can see I'm currently joining each table via subquery and need only the latest entry so i need an additional subquery to get the max(lfdnr).
This all works good so far but i wanna know if there is a more efficient way to perform this.
In my main select I need to be able to address each column from each of these tables.
Do you guys have any suggestions ?
This currently runs in 1.3 sec on 13k rows to get a decent boost this should get down to 0.1 sec or so. Again this is only one problem in a bigger statement full of unefficient declarations.

It's not possible to optimize a SQL query. Oracle is going to take your query and determine an execution plan that it uses to determine the information you've asked for. You might completely rewrite the query and, if it leads to the same execution plan, you'll get exactly the same performance. To tune a query, you need to determine the execution plan.
You may well benefit from an index on each of these tables, on the (fi_nr, fklz) columns or possibly even (fi_nr, fklz, lfdnr), depending on how selective that gets. There's no way to tell in advance from this information, you'll just have to try.
You should also remove the select * and select only the columns you want. If it's possible to get just the needed information from an index, Oracle will not need to retrieve the actual table row.

replace the left joins from :
LEFT OUTER JOIN (
SELECT fi_nr,fklz,rvc_status,lfdnr,txt FROM rc0531 temp
WHERE lfdnr = (SELECT MAX(lfdnr) FROM rc0531 WHERE fi_nr = temp.fi_nr AND fklz = temp.fklz)
) SUB_TABLE1
ON F.fklz = SUB_TABLE1.fklz
AND F.fi_nr = SUB_TABLE1.fi_nr
to this:
LEFT OUTER JOIN (
SELECT fi_nr,fklz,rvc_status,lfdnr,txt FROM rc0531 inner join
(SELECT fi_nr, fklz, MAX(lfdnr) lfdnr FROM rc0531 group by fi_nr, fklz)x on x.fi_nr=rc0531.fi_nr and x.fklz=rc0531.fklz and x.lfdnr=rc0531.lfdnr
) SUB_TABLE1
ON F.fklz = SUB_TABLE1.fklz
AND F.fi_nr = SUB_TABLE1.fi_nr
let me know if it got to 0.1 sec, i think it will go

Related

Difference in the following using Hive Left Outer Join

I had an issue with the LEft outer join and my query was little bit wierd. Tried searching for it , but did not get any clues.
This is happening due to were clause has been added.
The query below is not working as expected.
SELECT count(*) from
BN_DATA_TEMP.EDS_DELIVERYITEM_GBQ_SAMPLE_DATA A
LEFT OUTER JOIN BN_DATA_TEMP.TBL_EDS_DELIVERYITEMS_DATES B
on (
to_Date(substr(A.LASTUPDATEDTIME,1,10)) = '2020-05-31'
and to_Date(substr(B.LASTUPDATEDTIME,1,10)) = '2020-05-31'
and A.deliveryid = B.deliveryid )
where B.deliveryid is null;
Trying to get Records in A but not in B. But the count is very different and it is wrong.
I need to consider only the dates with 2020-05-31 from both the tables.
Modified like the below and it gave the correct result.
select count(*) from
(SELECT * from
BN_DATA_TEMP.EDS_DELIVERYITEM_GBQ_SAMPLE_DATA where to_Date(substr(LASTUPDATEDTIME,1,10)) = '2020-05-31' ) A
LEFT OUTER JOIN
(select * from
BN_DATA_TEMP.TBL_EDS_DELIVERYITEMS_DATES where to_Date(substr(LASTUPDATEDTIME,1,10)) = '2020-05-31' ) B
on A.deliveryid = B.deliveryid
where B.deliveryid is null;
I have never use much of Left outer joins for Data validation. So thought of posting over here and learn the same.
Thanks in Advance.

MAX DATE with Multiple tables/Inner Joins in Toad/Oracle

I have only been using Toad/Oracle for a few weeks so am still learning coding etc I have knowledge of SQL Code in Access and trying to now learn Oracle.
I need to return the max date from UCMRBILDAT from tbl BIC/AZUCDMO0100 but only from contracts which are contained in linked tbl LH_DAT
I have also tried a having MAX UCMRBILDAT but this didnt work.
UCMRBILDAT (/BIC/AZUCDMO0100)
UC_MRESULT (/BIC/AZUCDMO0100)
UC_MRSTAT (/BIC/AZUCDMO0100
UC_MRCAT (/BIC/AZUCDMO0100)
CONTRACT_NUMBER (LH_DAT)
UC_MR_NUMB (/BIC/AZUCDMO0100) + (/BIC/AZUCDMO0200)
SELECT UCMRBILDAT,
UC_MRESULT,
UC_MRSTAT,
UC_MRCAT
FROM LH_DAT
( SELECT CONTRACT_NUMBER, MAX (UCMRBILDAT) MXBD
FROM SAPSR3."/BIC/AZUCDMO0100"
GROUP BY CONTRACT_NUMBER) GMR
LEFT OUTER JOIN SAPSR3."/BIC/AZUCDMO0200"
ON (CONTRACT_NUMBER = UCCONTRACT)
INNER JOIN SAPSR3."/BIC/AZUCDMO0100"
ON ("/BIC/AZUCDMO0200".UC_MR_NUMB = "/BIC/AZUCDMO0100".UC_MR_NUMB)
WHERE CONTRACT_NUMBER = '2000014420'
AND UCMRBILDAT = MXBD
AND MR.CONTRACT_NUMBER = GMR.CONTRACT_NUMBER
Max bill date from BIC/AZUCDMO0100 but only for contracts contained in table LH_DAT
EDIT NEED MAX DATE FOR UCMRBILDAT ON BELOW SCRIPT
SELECT CONTRACT_NUMBER,
UCMRBILDAT,
UC_MRESULT,
UC_MRCAT
FROM LH_DAT
LEFT OUTER JOIN SAPSR3."/BIC/AZUCDMO0200"
ON (CONTRACT_NUMBER = UCCONTRACT)
INNER JOIN SAPSR3."/BIC/AZUCDMO0100"
ON ("/BIC/AZUCDMO0200".UC_MR_NUMB = "/BIC/AZUCDMO0100".UC_MR_NUMB)
WHERE CONTRACT_NUMBER = '2000014420'
AND "/BIC/AZUCDMO0200".SOURSYSTEM = 'SP'
AND "/BIC/AZUCDMO0200".UCDELE_IND <> 'X'
To get the max value of "BIC/AZUCDMO0100".UCMRBILDAT where there's a linked value from LH_DAT you'd want to use:
SELECT MAX(ba.UCMRBILDAT)
FROM SAPSR3."BIC/AZUCDMO0100" ba
INNER JOIN LH_DAT ld
ON ld.some_field = ba.some_field
There must be fields which link "BIC/AZUCDMO0100" and LH_DAT together, but in your query they're not specified. Find those fields, plug them in to the query above, and you should get the result you're looking for.

Cognos 8 Query to find all Report and Column Names

I want to query the meta data in Cognos 8 to find all report and column names. If possible, I'd like to include the column definitions.
Can I do this using a Cognos report or do I need to query some repository?
Thanks.
You can select a list of reports from the content store with the following query:
SELECT CMOBJNAMES_BASE.NAME AS ObjName, CMOBJECTS.PCMID, CMCLASSES.NAME AS ClassName, CMOBJPROPS7.spec
FROM CMOBJECTS
JOIN CMOBJNAMES_BASE ON CMOBJECTS.CMID = CMOBJNAMES_BASE.CMID
JOIN CMCLASSES ON CMOBJECTS.CLASSID = CMCLASSES.CLASSID
LEFT JOIN CMOBJPROPS7 ON CMOBJECTS.CMID = CMOBJPROPS7.CMID
WHERE CMOBJECTS.CLASSID IN (10, 37)
ORDER BY CMOBJECTS.PCMID;
I use that in Cognos 10. I believe in cognos 8 the CMOBJNAMES_BASE table is actually named 'CMOBJNAMES' without the _BASE.
UPDATE: Has been tested and works in Cognos 11r9.
The Report metadata is stored in the 'SPEC' column of CMOBJPROPS7 as XML. You can parse this XML in order to strip out the columns used in the report. It will not be a simple task.
If you have time but not money, you can write your own code to parse that XML. If you have more money than time, you can buy a 3rd party program to accomplish this, such as Motio or BSP Metamanager.
The query above is less useful for building a clean list of columns, but great for searching for specific data items. For example, you have column you are wanting to change in a data source, but you are not sure which report uses that column. Run the query above, and search for the data item. It will be embedded within the XML in the Cognos MDX format, ie. [Presentation View].[Sales Summary].[Sales]
EDIT: As requested below, here is a query that includes folder paths.
-- List of Reports, the folder they are in, and the package they are using
select distinct temp2.name as package,temp1.folder,temp1.name from
(SELECT temp.PARENTNAME AS FOLDER,CMOBJECTS.PCMID,CMOBJNAMES.CMID, CMOBJNAMES.LOCALEID, CMOBJNAMES.MAPDLOCALEID, CMOBJNAMES.ISDEFAULT, CMOBJNAMES.NAME,
CMOBJECTS.CLASSID
FROM CMOBJNAMES INNER JOIN
CMOBJECTS ON CMOBJNAMES.CMID = CMOBJECTS.CMID
INNER JOIN
(SELECT P.CMID AS PARENT,P.NAME AS PARENTNAME FROM CMOBJNAMES P where P.LOCALEID between 24 and 52) temp
ON CMOBJECTS.PCMID = TEMP.PARENT
WHERE (CMOBJECTS.CLASSID = 10)
AND SUBSTR(TEMP.PARENTNAME,1,1) NOT IN ('1','2','3','4','5','6','7','8','9') AND
TEMP.PARENTNAME NOT LIKE 'Backup%') temp1
inner join
(SELECT CMREFNOORD1.CMID AS PID, CMREFNOORD1.REFCMID, CMOBJNAMES.NAME
FROM CMREFNOORD1 INNER JOIN
CMOBJECTS ON CMREFNOORD1.REFCMID = CMOBJECTS.CMID INNER JOIN
CMOBJNAMES ON CMOBJECTS.CMID = CMOBJNAMES.CMID
WHERE (CMREFNOORD1.PROPID = 31 AND CMOBJNAMES.LOCALEID between 24 and 52)) temp2
on temp1.cmid = temp2.pid and LOCALEID between 24 and 52;
Not sure if this will help anybody, but our version doesn't have a table named CMOBJNAMES_BASE.
This is what works for me:
select ob2.cmid, c.name as classname, n.name as objectname, o.DELIVOPTIONS as deliveryoptions, z2.name as owner
from CMOBJPROPS2 p
inner join CMOBJPROPS26 o on p.cmid=o.cmid
inner join CMOBJECTS ob on ob.cmid=o.cmid
inner join CMOBJECTS ob2 on ob.pcmid=ob2.cmid
inner join CMOBJNAMES n on n.cmid=ob2.cmid
inner join CMCLASSES c on ob2.classid=c.classid
left join CMREFNOORD2 z1 on z1.cmid = p.cmid
left join CMOBJPROPS33 z2 on z2.CMID = z1.REFCMID
where ACTIVE = 1 order by z2.name, objectName

PL SQL - Join 2 tables and return max from right table

Trying to retrive the MAX doc in the right table.
SELECT F43.PDDOCO,
F43.PDSFXO,
F43.PDLNID,
F43.PDAREC/100 As Received,
F431.PRAREC/100,
max(F431.PRDOC)
FROM PRODDTA.F43121 F431
LEFT OUTER JOIN PRODDTA.F4311 F43
ON
F43.PDKCOO=F431.PRKCOO
AND F43.PDDOCO=F431.PRDOCO
AND F43.PDDCTO=F431.PRDCTO
AND F43.PDSFXO=F431.PRSFXO
AND F43.PDLNID=F431.PRLNID
WHERE F431.PRDOCO = 401531
and F431.PRMATC = 2
and F43.PDLNTY = 'DC'
Group by
F43.PDDOCO,
F43.PDSFXO,
F43.PDLNID,
F43.PDAREC,
F431.PRAREC/100
This query is still returning the two rows in the right table. Fairly new to SQL and struggling with the statement. Any help would be appreciated.
Without seeing your data it is difficult to tell where the problem might so I will offer a few suggestions that could help.
First, you are joining with a LEFT JOIN on the PRODDTA.F4311 but you have in the WHERE clause a filter for that table. You should move the F43.PDLNTY = 'DC' to the JOIN condition. This is causing the query to act like an INNER JOIN.
Second, you can try using a subquery to get the MAX(PRDOC) value. Then you can limit the columns that you are grouping on which could eliminate the duplicates. The query would them be similar to the following:
SELECT F43.PDDOCO,
F43.PDSFXO,
F43.PDLNID,
F43.PDAREC/100 As Received,
F431.PRAREC/100,
F431.PRDOC
FROM PRODDTA.F43121 F431
INNER JOIN
(
-- subquery to get the max
-- then group by the distinct columns
SELECT PDKCOO, max(PRDOC) MaxPRDOC
FROM PRODDTA.F43121
WHERE PRDOCO = 401531
and PRMATC = 2
GROUP BY PDKCOO
) f2
-- join the subquery result back to the PRODDTA.F43121 table
on F431.PRDOC = f2.MaxPRDOC
AND F431.PDKCOO = f2.PDKCOO
LEFT OUTER JOIN PRODDTA.F4311 F43
ON F43.PDKCOO=F431.PRKCOO
AND F43.PDDOCO=F431.PRDOCO
AND F43.PDDCTO=F431.PRDCTO
AND F43.PDSFXO=F431.PRSFXO
AND F43.PDLNID=F431.PRLNID
AND F43.PDLNTY = 'DC' -- move this filter to the join instead of the WHERE
WHERE F431.PRDOCO = 401531
and F431.PRMATC = 2
If you provide your table structures and some sample data, it will be easier to determine the issue.

Tsql: what is the best way to retrieve some records with a specific criteria?

I have a table (Cars) which saves some characteristics car like EngineNo, LastProductionStepId, NodyNo, ...
Besides, I have another table (CarSteps) which saves all steps that a specific car should pass during its manufacturing proccess like Engine Assigning(Id = 2), Engraving(3), PrePaint(4), Paint(5), AfterPaint(6), Confirmation(7), Delivery(8)
I would like to get all cars that are between PrePaint and Confirmation at the moment:
select cr.Id, cr.BodyNo, cr.LastStepId
from Cars cr WITH (NOLOCK)
inner join CarSteps steps WITH (NOLOCK) on cr.Id = trace.CarId and cr.LastStepId=trace.StepId
where
cr.LastStepId >= 4
AND cr.[Status] = 1
AND steps.[Status] = 1
AND not exists ( select *
from CarSteps steps1 WITH (NOLOCK)
where steps1.CarId = cr.Id
AND steps1.StepId >= 7 AND steps1.Status = 1
)
Because CarSteps has many records (44 million) the query is slow .
what is your opinion? is there any better way to get those cars?
Looking at your query I see a join from Cars to CarSteps, I see you joining to trace.CarId and trace.StepId is. trace is not defined in your query.
from
Cars cr WITH (NOLOCK) inner join
CarSteps steps WITH (NOLOCK) on
cr.Id = trace.CarId and
cr.LastStepId=trace.StepId
I may be able to help if i have better understand the fully query.
What does the execution plan reveal?

Resources