Query with NULL

  • Follow


Hi,

I have a lot of queries like below:
>select * from bxat.no5
>WHERE (no0_session_id = :wp18 OR :wp18 IS NULL)
>AND (tbname = :wp19 OR :wp19 IS NULL)
so an access full on no5

How can I speed up this query ?
Thanks in advance
(Oracle 9.2.0.6)


0
Reply bob123 (66) 12/29/2009 8:19:22 PM

On Dec 29, 12:19=A0pm, "bob123" <bob...@gmail.com> wrote:
> Hi,
>
> I have a lot of queries like below:>select * from bxat.no5
> >WHERE (no0_session_id =3D :wp18 OR :wp18 IS NULL)
> >AND (tbname =3D :wp19 OR :wp19 IS NULL)
>
> so an access full on no5
>
> How can I speed up this query ?
> Thanks in advance
> (Oracle 9.2.0.6)

Bob,

If the issue is that the optimizer is choking on the ORs you might,
barring adding indexes, want to consider changing your query from the
present set of ORs to a set of simpler UNIONs, e.g.,

select *
from bxat.no5
WHERE no0_session_id =3D :wp18
UNION
select *
from bxat.no5
WHERE
wp18 IS NULL
UNION
..
..
..


--Jeff
0
Reply jefftyzzer 12/29/2009 9:39:06 PM


Let's look at simplified query:

select * from bxat.no5
WHERE (no0_session_id = :wp18 OR :wp18 IS NULL)

We assume that there is an index on "no0_session_id" and this index
has good selectivity.

If :wp18 has a value then we want the query to use index. If :wp18 is
NULL then all rows in the table
satisfy WHERE clause so we want the query to use full table scan.


Let's simplify the query even further and look at the plans:

SQL> select * from bxat.no5 where no0_session_id = :wp18;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=13)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'NO5' (Cost=1 Card=1
Bytes=13)
   2    1     INDEX (UNIQUE SCAN) OF 'NO5_IDX01' (UNIQUE) (Cost=3
Card=1)


SQL> select * from bxat.no5 where :wp18 is null;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=84653 Card=42845620
Bytes=85691240)
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'NO5' (Cost=84653 Card=42845620
Bytes=85691240)


Note FILTER above. You get the same plan if you run query with
condition "1=0". While plan contains full table scan Oracle doesn't
actually run it if condition is false - it is stopped by FILTER.

Now the original simplified query:

SQL> select * from bxat.no5 where no0_session_id = :wp18 or :wp18 is
null;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=84653 Card=2142282
Bytes=27849666)
   1    0   TABLE ACCESS (FULL) OF 'NO5' (Cost=84653 Card=2142282
Bytes=27849666)

Here "FILTER" disappeared, so Oracle always runs the full scan.

Finally the version with UNION:

SQL> select * from bxat.no5 where no0_session_id = :wp18
  2  union
  3  select * from bxat.no5 where :wp18 is null;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=156184 Card=42845621
Bytes=85691253)
   1    0   SORT (UNIQUE) (Cost=156184 Card=42845621 Bytes=85691253)
   2    1     UNION-ALL
   3    2       TABLE ACCESS (BY INDEX ROWID) OF 'NO5' (Cost=1 Card=1
Bytes=13)
   4    3         INDEX (UNIQUE SCAN) OF 'NO5_IDX01' (UNIQUE) (Cost=3
Card=1)
   5    2       FILTER
   6    5         TABLE ACCESS (FULL) OF 'NO5' (Cost=84653
Card=42845620 Bytes=85691240)

So the trick is to keep the FILTER.



0
Reply vsevolod 12/30/2009 12:33:35 AM

On Dec 29, 9:19=A0pm, "bob123" <bob...@gmail.com> wrote:
> I have a lot of queries like below:
> select * from bxat.no5
> WHERE (no0_session_id =3D :wp18 OR :wp18 IS NULL)
> AND (tbname =3D :wp19 OR :wp19 IS NULL)
>
> so an access full on no5
>
> How can I speed up this query ?
> Thanks in advance
> (Oracle 9.2.0.6)

One possible way to achieve this without the explicit usage of UNION
suggested is the rewrite with NVL or DECODE as outlined here:

http://jonathanlewis.wordpress.com/2007/01/09/conditional-sql/

Which would look something like this for your particular query:

select
        *
from
        bxat.no5
WHERE
        no0_session_id =3D nvl(:wp18, no0_session_id)
AND     tbname =3D nvl(:wp19, tbname)

The optimizer can then transform this using a concatenation
transformation to make potential use of an index.

Note however that this optimisation doesn't perform multiple
concatenation transformations for multiple similar predicates, as can
be seen here:

http://jonathanlewis.wordpress.com/2007/02/14/conditional-sql-2/

so it might not work that well in your particular case.

and you need to be aware that this re-write is not exactly the same as
your statement if the columns are nullable since the re-write filters
out any rows where the column is null whereas your original statement
does not.

Regards,
Randolf

Oracle related stuff blog:
http://oracle-randolf.blogspot.com/

Co-author of the forthcoming "OakTable Expert Oracle Practices" book:
http://www.apress.com/book/view/1430226684
http://www.amazon.com/Expert-Oracle-Practices-Database-Administration/dp/14=
30226684
0
Reply Randolf 12/30/2009 3:29:26 PM

On Dec 29, 4:39=A0pm, jefftyzzer <jefftyz...@sbcglobal.net> wrote:
> On Dec 29, 12:19=A0pm, "bob123" <bob...@gmail.com> wrote:
>
> > Hi,
>
> > I have a lot of queries like below:>select * from bxat.no5
> > >WHERE (no0_session_id =3D :wp18 OR :wp18 IS NULL)
> > >AND (tbname =3D :wp19 OR :wp19 IS NULL)
>
> > so an access full on no5
>
> > How can I speed up this query ?
> > Thanks in advance
> > (Oracle 9.2.0.6)
>
> Bob,
>
> If the issue is that the optimizer is choking on the ORs you might,
> barring adding indexes, want to consider changing your query from the
> present set of ORs to a set of simpler UNIONs, e.g.,
>
> select *
> from bxat.no5
> WHERE no0_session_id =3D :wp18
> UNION
> select *
> from bxat.no5
> WHERE
> wp18 IS NULL
> UNION
> .
> .
> .
>
> --Jeff

Being that :wp18 and :wp19 are program bind variables then depending
on which variables have a value they are actually 4 different
conditions: return all rows when neither variable has a value, return
matching rows for wp18 when wp19 is null,, return matching rows for
wp19 when wp18 is null, and return only rows that match both wp18 and
wp19 when both have a value.

While one query could be written to handle this set of requirements, I
would consider to make the correct desired result clear for future
maintenance programmers just coding a simple SQL statement for each
condition within a IF structure within the program.  That way, based
on which variables have values the correct desired SQL statement is
submitted.

I think taking the simple approach would likely greatly reduce the
chance the CBO chooses anything but the optimal plan for the submitted
query and again the simplier SQL will be unlikely to confuse any
future maintenance programmer as to what result is desired.

Which approach is best is going to depend on the environment:
additional complexity in where clause conditions, how static the
application is expected to be, etc....  It may be that each approach
will prove beneficial depending on the program in question.

HTH -- Mark D Powell --




0
Reply Mark 12/30/2009 3:36:19 PM

On Dec 30, 7:36=A0am, Mark D Powell <Mark.Powe...@hp.com> wrote:

>
> While one query could be written to handle this set of requirements, I
> would consider to make the correct desired result clear for future
> maintenance programmers just coding a simple SQL statement for each
> condition within a IF structure within the program. =A0That way, based
> on which variables have values the correct desired SQL statement is
> submitted.
>
> I think taking the simple approach would likely greatly reduce the
> chance the CBO chooses anything but the optimal plan for the submitted
> query and again the simplier SQL will be unlikely to confuse any
> future maintenance programmer as to what result is desired.

I'd go for a CASE over IF for future maintenance, if it is a tossup.
Had one of those yesterday, was so glad it was CASE - took 5 minutes,
most of which was source control/comments, would have needed much more
work if IF's.  The issue is often that over time, the IF's get more
complicated - people can't resist nesting and ELSE.  This usually
arises from changing business rules to account for all the situations
not thought of (or admitted) originally.  I know, you'd think there
would be no difference since they can be logically equivalent... but
programmer behavior removes the equivalency.

jg
--
@home.com is bogus.
http://100waystokillapeep.blogspot.com/2009/04/psycho-bunny-killer.html

0
Reply joel 12/30/2009 5:17:12 PM

OK thanks all ...
The problem is that i can't rewrite the code
it's a third party software ...
any clue ? 


0
Reply bob123 12/31/2009 8:58:32 AM

On 12/31/2009 09:58 AM, bob123 wrote:
> OK thanks all ...
> The problem is that i can't rewrite the code
> it's a third party software ...
> any clue ? 

Difficult...  You can't easily use stored outlines because those would 
be optimal for a single of the four cases.  You may get away with 
outlines in four different categories but then you somehow need to 
switch between them before the statement is executed.  I guess 
everything you attempt will soon get messy.  Is there no chance to get 
the vendor of the application to change it?  Maybe you can file it as 
bug and get a resolution via your support contract.

Good luck!

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
0
Reply Robert 12/31/2009 11:24:49 AM

On Tue, 29 Dec 2009 21:19:22 +0100, bob123 wrote:

>>select * from bxat.no5
>>WHERE (no0_session_id = :wp18 OR :wp18 IS NULL) AND (tbname = :wp19 OR
>>:wp19 IS NULL)

I would advise against checking whether an external variable is NULL in 
the SQL. I am aware that Hibernate sometimes does that, but that can be 
reworked.



-- 
http://mgogala.byethost5.com
0
Reply Mladen 12/31/2009 1:31:54 PM

On Wed, 30 Dec 2009 07:36:19 -0800, Mark D Powell wrote:

> Being that :wp18 and :wp19 are program bind variables then depending on
> which variables have a value they are actually 4 different conditions:
> return all rows when neither variable has a value, return matching rows
> for wp18 when wp19 is null,, return matching rows for wp19 when wp18 is
> null, and return only rows that match both wp18 and wp19 when both have
> a value.

Plus the fact that the job checking whether an external variable is null 
really belongs to the programming interface and not the database.



-- 
http://mgogala.byethost5.com
0
Reply Mladen 12/31/2009 1:33:35 PM

On Dec 31, 8:33=A0am, Mladen Gogala <gogala.mla...@gmail.com> wrote:
> On Wed, 30 Dec 2009 07:36:19 -0800, Mark D Powell wrote:
> > Being that :wp18 and :wp19 are program bind variables then depending on
> > which variables have a value they are actually 4 different conditions:
> > return all rows when neither variable has a value, return matching rows
> > for wp18 when wp19 is null,, return matching rows for wp19 when wp18 is
> > null, and return only rows that match both wp18 and wp19 when both have
> > a value.
>
> Plus the fact that the job checking whether an external variable is null
> really belongs to the programming interface and not the database.
>
> --http://mgogala.byethost5.com

Since the OP has now informed us the SQL is part of a package and
cannot be changed I think the OP's options are limited.  Stored
outlines might be of use but I think it is unlikely since one possible
condition in the SQL as posted requires a full table scan that I do
not think there is any way around without changing the SQL.

I suggest complaining to the vendor about the manner in which the SQL
is written and its effect on performance.  I have some limited
experience with DB2 UDB and a liittle more experience with SQL Server
so I believe it is safe to say that having the program issue simplier
SQL for each desired result set based on program variables having
values or not would be more effiicient in all three systems than
having to have that check made in the SQL.  I think this last is
pretty much in agreement with Mgogla's previous post in that some
actions belong in the program and some in the database.

HTH -- Mark D Powell --


0
Reply Mark 12/31/2009 4:30:45 PM

bob123 (bob123@gmail.com) wrote:
: Hi,

: I have a lot of queries like below:
: >select * from bxat.no5
: >WHERE (no0_session_id = :wp18 OR :wp18 IS NULL)
: >AND (tbname = :wp19 OR :wp19 IS NULL)
: so an access full on no5

: How can I speed up this query ?
: Thanks in advance
: (Oracle 9.2.0.6)

1. Try adding the not null explicitly to each section, I seem to recall 
that helps the optimizer recognize what to do.

	select * from bxat.no5
	WHERE 
	( :wp18 IS not NULL and no0_session_id = :wp18 OR :wp18 IS NULL)
	  ---------------------
	AND 
	( :wp19 IS not NUL and tbname = :wp19 OR :wp19 IS NULL)
	  ---------------------

then you could try using union.  Someone suggested that though an
illustration of it didn't seem to use it correctly

	select * from bxat.no5
	WHERE 
	( :wp18 IS not NULL and no0_session_id = :wp18 OR :wp18 IS NULL)
	union
	select * from bxat.no5
	WHERE 
	( :wp19 IS not NUL and tbname = :wp19 OR :wp19 IS NULL)

if the union helps but the main part of the query is complicated then put 
it in a with clause (my example may have the syntax wrong)

	with
	( select * from bxat.no5
	  where complicated stuff in common
	) as the_query
	select * from the_query where
	( :wp18 IS not NULL and no0_session_id = :wp18 OR :wp18 IS NULL)
	union
	select * from the_query where
	( :wp19 IS not NUL and tbname = :wp19 OR :wp19 IS NULL)


0
Reply yf110 12/31/2009 6:24:04 PM

On Thu, 31 Dec 2009 08:30:45 -0800, Mark D Powell wrote:


> Since the OP has now informed us the SQL is part of a package and cannot
> be changed I think the OP's options are limited.

In other words, the OP is screwed. Happy New Year.



-- 
http://mgogala.byethost5.com
0
Reply Mladen 1/1/2010 4:55:46 AM

On Thu, 31 Dec 2009 08:30:45 -0800, Mark D Powell wrote:

> Since the OP has now informed us the SQL is part of a package and cannot
> be changed I think the OP's options are limited.  Stored outlines might
> be of use but I think it is unlikely since one possible condition in the
> SQL as posted requires a full table scan that I do not think there is
> any way around without changing the SQL.

In 10G there is DBMS_ADVANCED_REWRITE package which can be used to 
rewrite the bad SQL even if there is no access to the source. The 
procedure in the package is "DECLARE_REWRITE_EUIVALENCE". A brief 
example is available here:

http://www.blogskies.com/2009/07/dbmsadvancedrewrite.html

Somewhat more detailed article is here:

http://www.oracle-base.com/articles/10g/dbms_advanced_rewrite.php




-- 
http://mgogala.freehostia.com
0
Reply Mladen 1/3/2010 8:06:12 PM

On Jan 3, 9:06=A0pm, Mladen Gogala <gogala.mla...@gmail.com> wrote:
> In 10G there is DBMS_ADVANCED_REWRITE package which can be used to
> rewrite the bad SQL even if there is no access to the source. The
> procedure in the package is "DECLARE_REWRITE_EUIVALENCE". A brief
> example is available here:

I haven't suggested this because in its present form in 10g (haven't
tested it yet in newer releases, but I think these restrictions still
apply) it has some limitations that don't make it suitable for OLTP
like applications. In particular it doesn't support SQLs that contain
bind variables and therefore it is probably not applicable to the
statements mentioned here.

See Metalink (sorry, MOS) Note 392214.1

Dion Cho has also two interesting articles about this package:

http://dioncho.wordpress.com/2009/03/06/optimizing-unoptimizeable-sql-dbms_=
advanced_rewrite/

http://dioncho.wordpress.com/2009/10/13/dbms_advanced_rewrite-and-dml/

Regards,
Randolf

Oracle related stuff blog:
http://oracle-randolf.blogspot.com/

Co-author of the forthcoming "OakTable Expert Oracle Practices" book:
http://www.apress.com/book/view/1430226684
http://www.amazon.com/Expert-Oracle-Practices-Database-Administration/dp/14=
30226684
0
Reply Randolf 1/4/2010 10:52:53 AM

On Mon, 04 Jan 2010 02:52:53 -0800, Randolf Geist wrote:


> See Metalink (sorry, MOS) Note 392214.1

I'm aware of that paper, but that stinking pile of ... stuff ain't 
working... again.

> 
> Dion Cho has also two interesting articles about this package:
> 
> http://dioncho.wordpress.com/2009/03/06/optimizing-unoptimizeable-sql-
dbms_advanced_rewrite/
> 
> http://dioncho.wordpress.com/2009/10/13/dbms_advanced_rewrite-and-dml/
> 
> Regards,
> Randolf

I wasn't aware of these two excellent articles. Thanks again.


-- 
http://mgogala.byethost5.com
0
Reply Mladen 1/4/2010 3:59:30 PM

On 29 dec 2009, 22:39, jefftyzzer <jefftyz...@sbcglobal.net> wrote:
> On Dec 29, 12:19=A0pm, "bob123" <bob...@gmail.com> wrote:
>
> > Hi,
>
> > I have a lot of queries like below:>select * from bxat.no5
> > >WHERE (no0_session_id =3D :wp18 OR :wp18 IS NULL)
> > >AND (tbname =3D :wp19 OR :wp19 IS NULL)
>
> > so an access full on no5
>
> > How can I speed up this query ?
> > Thanks in advance
> > (Oracle 9.2.0.6)
>
> Bob,
>
> If the issue is that the optimizer is choking on the ORs you might,
> barring adding indexes, want to consider changing your query from the
> present set of ORs to a set of simpler UNIONs, e.g.,
>
> select *
> from bxat.no5
> WHERE no0_session_id =3D :wp18
> UNION
> select *
> from bxat.no5
> WHERE
> wp18 IS NULL
> UNION
> .
> .
> .
>
> --Jeff


That should be a "UNION ALL".

a. performance  (saves an expensive SORT)
B. to get all rows, even if there are duplicates
0
Reply Gerard 1/5/2010 12:32:35 PM

On Jan 5, 4:32=A0am, "Gerard H. Pille" <ghpi...@hotmail.com> wrote:
> On 29 dec 2009, 22:39, jefftyzzer <jefftyz...@sbcglobal.net> wrote:
>
>
>
> > On Dec 29, 12:19=A0pm, "bob123" <bob...@gmail.com> wrote:
>
> > > Hi,
>
> > > I have a lot of queries like below:>select * from bxat.no5
> > > >WHERE (no0_session_id =3D :wp18 OR :wp18 IS NULL)
> > > >AND (tbname =3D :wp19 OR :wp19 IS NULL)
>
> > > so an access full on no5
>
> > > How can I speed up this query ?
> > > Thanks in advance
> > > (Oracle 9.2.0.6)
>
> > Bob,
>
> > If the issue is that the optimizer is choking on the ORs you might,
> > barring adding indexes, want to consider changing your query from the
> > present set of ORs to a set of simpler UNIONs, e.g.,
>
> > select *
> > from bxat.no5
> > WHERE no0_session_id =3D :wp18
> > UNION
> > select *
> > from bxat.no5
> > WHERE
> > wp18 IS NULL
> > UNION
> > .
> > .
> > .
>
> > --Jeff
>
> That should be a "UNION ALL".
>
> a. performance =A0(saves an expensive SORT)
> B. to get all rows, even if there are duplicates

With respect, while I readily agree that UNION ALL is likely faster
given that it obviates the need for a duplicate-eliminating sort, I
think the UNION is necessary precisely because it eliminates
duplicates. My goal was to suggest an alternate syntax that was
semantically equivalent to the OP's original query, which wouldn't
have returned duplicate rows given that it made only a single pass
through the "bxat.no5" table (whether the "bxat.no5" table contains
duplicates is a different matter altogether).

Regards,

--Jeff
0
Reply jefftyzzer 1/6/2010 3:13:12 AM

bob123 wrote:
> OK thanks all ...
> The problem is that i can't rewrite the code
> it's a third party software ...
> any clue ? 
> 
> 
written in a far and distant country, where the programmers had
no clue about the data model.

Speculating, of course, but I've seen these constructs before.

Bet ya those columns are NOT NULL.
-- 

Regards,
Frank van Bortel
0
Reply Frank 1/6/2010 11:14:56 AM

18 Replies
314 Views

(page loaded in 0.186 seconds)

Similiar Articles:


















7/25/2012 1:20:56 PM


Reply: