surenda
asked on
Query performance - Alternate for instr
Hi all,
I need help in tuning the below query, i am using instr function in the query which is time consuming and creates lot of performance issue.
kinly let me know if we can use any other functions which will not have performance impact .
SELECT * FROM clnt_oc_disabled a,
ordcatalog b, ordcomponent c, tmp_entry d, tmp_entry e
WHERE a.ocid = b.ocid
AND b.ocid = c.ocid AND d.tmpid = 15
AND e.tmpid = d.tmpid AND e.nodeid = d.parentid
AND instr(','
||TRANSLATE(d.optionalitem s, ' ', ',')
||',', ','
||TO_CHAR(c.attvalmeid)
||',' ) > 0
I need help in tuning the below query, i am using instr function in the query which is time consuming and creates lot of performance issue.
kinly let me know if we can use any other functions which will not have performance impact .
SELECT * FROM clnt_oc_disabled a,
ordcatalog b, ordcomponent c, tmp_entry d, tmp_entry e
WHERE a.ocid = b.ocid
AND b.ocid = c.ocid AND d.tmpid = 15
AND e.tmpid = d.tmpid AND e.nodeid = d.parentid
AND instr(','
||TRANSLATE(d.optionalitem
||',', ','
||TO_CHAR(c.attvalmeid)
||',' ) > 0
You may want to also look at your Execution Plan.
Anytime you use functions of the left in the predicate it usually invalidates use of any indexes on the query which may lead to some ugly full table scans (not so bad if they are small tables, horrendous if they are very large tables).
Anytime you use functions of the left in the predicate it usually invalidates use of any indexes on the query which may lead to some ugly full table scans (not so bad if they are small tables, horrendous if they are very large tables).
forgive me but aren't you adding a comma into the concatenated string?
then using instr to locate a comma
(i.e. won't it always be true?)
also, could you review the table joins?
If I attempt to use ANSI syntax, it seems that tmp_entry d isn't actually joined and may be causing a cartesian product - I could be wrong but I'd check it anyway.
then using instr to locate a comma
(i.e. won't it always be true?)
also, could you review the table joins?
If I attempt to use ANSI syntax, it seems that tmp_entry d isn't actually joined and may be causing a cartesian product - I could be wrong but I'd check it anyway.
-- apply ANSI JOINS ?
SELECT *
FROM clnt_oc_disabled a
INNER JOIN ordcatalog b ON a.ocid = b.ocid
INNER JOIN ordcomponent c ON b.ocid = c.ocid
INNER JOIN tmp_entry d
INNER JOIN tmp_entry e ON e.tmpid = d.tmpid AND e.nodeid = d.parentid
WHERE
-- AND
AND d.tmpid = 15
-- AND
-- AND
AND instr(',' || TRANSLATE(d.optionalitems, ' ', ',')
|| ',', ','
|| TO_CHAR(c.attvalmeid)
|| ','
) > 0
ASKER
Basically i am trying to find c.attvalmeid from optionalitems, Optional item will be having value as "20636 28218" and attvalmeid will be having 28128 , so i am replacing empty space with comma in optionalitems and locate the string using instr .
I really would like you to check those joins still.
But perhaps just a LIKE would help? Below is a just a tiny sample - please ignore the use of cross join I'm not proposing you would use that. see this working at: http://sqlfiddle.com/#!4/e5413/3
But perhaps just a LIKE would help? Below is a just a tiny sample - please ignore the use of cross join I'm not proposing you would use that. see this working at: http://sqlfiddle.com/#!4/e5413/3
SELECT
d.*
, c.*
, '%' || c.ATTVALMEID || '%'
FROM ORDCOMPONENT c
CROSS JOIN TMP_ENTRY d -- nb: cross join just used for a tiny sample
WHERE d.OPTIONALITEMS LIKE ('%' || to_char(c.ATTVALMEID) || '%')
--DDL used (tiny sample as I said)
CREATE TABLE ORDCOMPONENT
(ATTVALMEID int)
;
INSERT ALL
INTO ORDCOMPONENT (ATTVALMEID)
VALUES (28128)
SELECT * FROM dual
;
CREATE TABLE TMP_ENTRY
(OPTIONALITEMS varchar2(100))
;
INSERT ALL
INTO TMP_ENTRY (OPTIONALITEMS)
VALUES ('20636 28128')
SELECT * FROM dual
;
ASKER
Thanks Paul, Again this is also taking much time in execution, it takes 37 seconds for me with like joins in the last if i remove that it executes just in seconds
SELECT a.ocid orderid,
(e.node_label || ' - ' || b.ordertext) as ordertext,
a.reasonid,
c.attvalmeid menentryid,
c.parentmeid,
d.nodeid,
d.parentid,
c.MedC,
d.node_label,
c.attname,
d.node_name
FROM clnt_oc_disabled a,
ordcatalog b,
ordcomponent c,
tmp_entry d,
tmp_entry e
WHERE a.ocid = b.ocid
AND NOT EXISTS
(SELECT *
FROM ordcomponent
WHERE ocid = c.ocid
AND parentmeid = c.parentmeid
AND NOT EXISTS
(SELECT *
FROM tmp_entry
WHERE tmpid = e.tmpid
AND parentid = e.nodeid
AND menentryid = attvalmeid
)
)
AND 0 =
(SELECT COUNT(*) FROM clnt_oc_disabled WHERE b.parent = clnt_oc_disabled.ocid
)
AND b.ocid = c.ocid
AND d.tmpid = 15
AND e.tmpid = d.tmpid
AND e.nodeid = d.parentid
AND c.attvalmeid IS NOT NULL
AND ((d.menentryid = c.attvalmeid)
--removing below 2 lines will execute fastly
or d.menentstr LIKE ('%' || to_char(c.ATTVALMEID) || '%')
OR d.OPTIONALITEMS LIKE ('%' || to_char(c.ATTVALMEID) || '%'))
SELECT a.ocid orderid,
(e.node_label || ' - ' || b.ordertext) as ordertext,
a.reasonid,
c.attvalmeid menentryid,
c.parentmeid,
d.nodeid,
d.parentid,
c.MedC,
d.node_label,
c.attname,
d.node_name
FROM clnt_oc_disabled a,
ordcatalog b,
ordcomponent c,
tmp_entry d,
tmp_entry e
WHERE a.ocid = b.ocid
AND NOT EXISTS
(SELECT *
FROM ordcomponent
WHERE ocid = c.ocid
AND parentmeid = c.parentmeid
AND NOT EXISTS
(SELECT *
FROM tmp_entry
WHERE tmpid = e.tmpid
AND parentid = e.nodeid
AND menentryid = attvalmeid
)
)
AND 0 =
(SELECT COUNT(*) FROM clnt_oc_disabled WHERE b.parent = clnt_oc_disabled.ocid
)
AND b.ocid = c.ocid
AND d.tmpid = 15
AND e.tmpid = d.tmpid
AND e.nodeid = d.parentid
AND c.attvalmeid IS NOT NULL
AND ((d.menentryid = c.attvalmeid)
--removing below 2 lines will execute fastly
or d.menentstr LIKE ('%' || to_char(c.ATTVALMEID) || '%')
OR d.OPTIONALITEMS LIKE ('%' || to_char(c.ATTVALMEID) || '%'))
while 37 seconds isn't stellar, is it faster than the instr approach at all?
& no matter what we do, the last part of this query will add some time most probably
are you totally convinced you joins are fine?
(please indicate yes/no and I can then stop hassling you on that point)
& no matter what we do, the last part of this query will add some time most probably
are you totally convinced you joins are fine?
(please indicate yes/no and I can then stop hassling you on that point)
ASKER
The joins are fine , thats y i showed you the full query now ,
but for both instr and like are taking same 37 seconds, since this query is running in loop for each tmpid i need to reduce as much time as possible,
can we achieve this using oracle xmltype ?
but for both instr and like are taking same 37 seconds, since this query is running in loop for each tmpid i need to reduce as much time as possible,
can we achieve this using oracle xmltype ?
yes, I see now, joins do exist. Observations:
'AND c.attvalmeid IS NOT NULL' is probably redundant - line 15
those text searches are part of the joining to tmp_entry d - lines 16,17,18 - which contributes substantially to the query execution
the first NOT EXISTS subquery could leverage the already joined alias c I think
the select count(*) subquery might return NULL, and that possibility is handled
This is how I see the current query
SELECT
a.ocid orderid
, (e.node_label || ' - ' || b.ordertext) AS ordertext
, a.reasonid
, c.attvalmeid menentryid
, c.parentmeid
, d.nodeid
, d.parentid
, c.MedC
, d.node_label
, c.attname
, d.node_name
FROM clnt_oc_disabled a
INNER JOIN ordcatalog b ON a.ocid = b.ocid
INNER JOIN ordcomponent c ON b.ocid = c.ocid AND c.attvalmeid IS NOT NULL -- the 'is not null' is probably redundant (see line 15)
INNER JOIN tmp_entry d ON c.attvalmeid = d.menentryid
OR d.menentstr LIKE ('%' || to_char(c.ATTVALMEID) || '%')
OR d.OPTIONALITEMS LIKE ('%' || to_char(c.ATTVALMEID) || '%')
INNER JOIN tmp_entry e ON e.tmpid = d.tmpid AND e.nodeid = d.parentid
WHERE d.tmpid = 15
AND NOT EXISTS (
SELECT *
FROM ordcomponent -- why can't you use table alias c instead?
WHERE ocid = c.ocid
AND parentmeid = c.parentmeid
AND NOT EXISTS (
SELECT *
FROM tmp_entry
WHERE tmpid = e.tmpid
AND parentid = e.nodeid
AND menentryid = attvalmeid
)
)
AND 0 = (
SELECT COUNT(*)
FROM clnt_oc_disabled
WHERE b.parent = clnt_oc_disabled.ocid
) -- what happens if this subquery is NULL?
the following is how I got there
SELECT
a.ocid orderid
, (e.node_label || ' - ' || b.ordertext) AS ordertext
, a.reasonid
, c.attvalmeid menentryid
, c.parentmeid
, d.nodeid
, d.parentid
, c.MedC
, d.node_label
, c.attname
, d.node_name
FROM clnt_oc_disabled a
INNER JOIN ordcatalog b ON a.ocid = b.ocid
INNER JOIN ordcomponent c ON b.ocid = c.ocid AND c.attvalmeid IS NOT NULL -- the 'is not null' is probably redundant (see line 15)
INNER JOIN tmp_entry d ON c.attvalmeid = d.menentryid
OR d.menentstr LIKE ('%' || to_char(c.ATTVALMEID) || '%')
OR d.OPTIONALITEMS LIKE ('%' || to_char(c.ATTVALMEID) || '%')
INNER JOIN tmp_entry e ON e.tmpid = d.tmpid AND e.nodeid = d.parentid
WHERE 1=1 -- see line 13
AND NOT EXISTS (
SELECT *
FROM ordcomponent -- why can't you use table alias c instead?
WHERE ocid = c.ocid
AND parentmeid = c.parentmeid
AND NOT EXISTS (
SELECT *
FROM tmp_entry
WHERE tmpid = e.tmpid
AND parentid = e.nodeid
AND menentryid = attvalmeid
)
)
AND 0 = (
SELECT COUNT(*)
FROM clnt_oc_disabled
WHERE b.parent = clnt_oc_disabled.ocid
) -- what happens if this subquery is NULL?
-- AND-- line 14
AND d.tmpid = 15
-- AND line 19
-- AND line 19
-- AND line 14
-- AND ( -- lines 16,17,18
-- () line 16
--removing below 2 lines will execute fastly
--OR d.menentstr LIKE ('%' || to_char(c.ATTVALMEID) || '%') -- line 17
--OR d.OPTIONALITEMS LIKE ('%' || to_char(c.ATTVALMEID) || '%') -- line 18
-- ) -- lines 16,17,18
So I will stop hassling you about the cartesian product as the join certainly exists. But that join certainly is not simple.
However without knowing a good deal more I don't think I can come up with a magic bullet.
Is that table TMP_ENTRY literally a temp table that you have constructed?
are these possibilities?
However without knowing a good deal more I don't think I can come up with a magic bullet.
Is that table TMP_ENTRY literally a temp table that you have constructed?
are these possibilities?
normalizing that data (i.e. splitting d.menentstr & d.OPTIONALITEMS) into multiple rows and storing those as INT so I can join to the c.ATTVALMEID
using a pipelined table function to split those strings into a pseudo table to simplify the join
using a pipelined table function to split those strings into a pseudo table to simplify the join
ASKER CERTIFIED SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
ASKER
Thanks Markgeer,
i can try function based index and see since redesigning will not be approved at this point .
i can try function based index and see since redesigning will not be approved at this point .
ASKER
Paul,
can you provide me some example for breaking into multipart string . or any example to split up into 2 different rows and join . i need to some how get away from string functions .
can you provide me some example for breaking into multipart string . or any example to split up into 2 different rows and join . i need to some how get away from string functions .
SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
I suspect TRANSLATE is also part of the performance issue by the way.
samples are really needed I think.