Skip to Main Content
  • Questions
  • How to optimize query for multiple joined tables

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Jay.

Asked: September 06, 2017 - 8:46 am UTC

Last updated: September 12, 2017 - 11:10 am UTC

Version: Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

Viewed 1000+ times

You Asked

Hi TOM,

I have this simplified version of a query. I would just like to check if there are other ways to further optimize the query especially on the subqueries. I'm limited to SQL only.

Reason for me thinking that this can be further optimized is that there are 2 similar subqueries, which I think doubles the resource and time it takes to run.

select acc.accid from acc
where ( select count(distinct(trunc(datetime)))
        from accact
  join accupd on accupd.accupdid = accact.accactid
  join disp on disp.dispid = accupd.dispid
  where accact.accactid = acc.accid
  and accupd.dictionary = 12345
  and disp.flag = 'Y'
  and trunc(accact.datetime) between (trunc(sysdate) - 3) and trunc(sysdate) - 1) >= 3
and   ( select count(distinct(trunc(datetime)))
        from accact
  join accupd on accupd.accupdid = accact.accactid
  join disp on disp.dispid = accupd.dispid
  where accact.accactid = acc.accid
  and accupd.dictionary = 12345
  and disp.flag = 'N'
  and trunc(accact.datetime) between (trunc(sysdate) - 3) and trunc(sysdate) - 1) = 0
;


Thanks

with LiveSQL Test Case:

and Chris said...

Having to subqueries with the same joins won't necessarily double the runtime. It depends on what data they process, indexes available, etc.

But it's unlikely to perform better than if you can access the tables just once!

It looks to me like you can do this by:

- Joining all the tables together
- Grouping by the acc.accid and disp.flag
- Adding a having clause to filter whether the counts return the correct values based on the flag value

For example:

select acc.accid, disp.flag, count(distinct(trunc(datetime))) 
from acc
join accact 
on   accact.accactid = acc.accid 
join accupd on accupd.accupdid = accact.accactid 
join disp on disp.dispid = accupd.dispid 
where accupd.dictionary = 12345 
and trunc(accact.datetime) between (trunc(sysdate) - 3) and trunc(sysdate) - 1
and disp.flag in ('Y', 'N')
group  by acc.accid, disp.flag
having case when disp.flag = 'Y' then count(distinct(trunc(datetime))) end >= 3
and    case when disp.flag = 'N' then count(distinct(trunc(datetime))) end = 0

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

grouping on disp.flag for those non existing records won't result to 0

Jay Gomez, September 08, 2017 - 3:05 am UTC

Hi Chris,
thanks for taking time to respond and simplifying the query. However, I have some concern.

ex.
Should there be no record for that acc.accid having a disp.flag = 'N', the grouping doesn't output a 0 thus the having clause cannot compare it.

and    case when disp.flag = 'N' then count(distinct(trunc(datetime))) end = 0


this as opposed to the original query where it just counts. if it founds none, it results to 0 which matches the 2nd subquery condition.

and   ( select count(distinct(trunc(datetime)))
        from accact
  join accupd on accupd.accupdid = accact.accactid
  join disp on disp.dispid = accupd.dispid
  where accact.accactid = acc.accid
  and accupd.dictionary = 12345
  and disp.flag = 'N'
  and trunc(accact.datetime) between (trunc(sysdate) - 3) and trunc(sysdate) - 1) = 0


I tried playing with the joins specifically on disp table to no avail. I might be missing something.
Chris Saxon
September 12, 2017 - 11:10 am UTC

I'm not following. Could you provide:

- create table
- insert into

showing exactly what's going on?

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.