Question

I have an oracle query that works like this:

SELECT * 
FROM VW_REQUIRED r
JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id

Required is a view detailing all required training materials, Actual is a view detailing the most recent taking of the given courses. Each of these queries by themselves take under 2 seconds to generate between 10k and 100k rows.

What I want to do is something like:

SELECT *
FROM VW_REQUIRED r
WHERE NOT EXISTS ( SELECT 1 FROM VW_ACTUAL a WHERE a.person_id = r.person_id AND a.target_id = r.target_id) 

and it takes more than 20 seconds (I didn't let it finish, because that is obviously too long.

So then I decided to do something different, I made the original JOIN a left join so it will show me all required training, and the actual training only if it exists.

That worked, and was super fast still.

But I want a list of just the courses where there is no actual training attached (i.e. the people we need to kick into gear and get their training...)

When I try something like

SELECT * 
FROM VW_REQUIRED r
LEFT JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id
WHERE r.person_id = null

I get no rows back. I'm not sure I can filter out the rows where I have no actual results. Normally I'd use WHERE NOT EXISTS but the performance on it was super slow (and I don't think I can put an index on a view...)

I've managed to make some changes that works, but it seems hacky and I'm sure there's a better solution.

SELECT
  who, where_from, mand, target_id, grace_period, date_taken
FROM (
    SELECT
      r.person_id who,
      r.where_from where_from,
      r.mand mand,
      r.target_id target_id
      r.grace_period grace_period,
      nvl(a.date_taken, to_date('1980/01/01','yyyy/mm/dd')) date_taken
    FROM VW_REQUIRED r
    LEFT JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id
)
WHERE date_taken = to_date('1980/01/01','yyyy/mm/dd')
Was it helpful?

Solution

I think you only mixed the table names. Can you change the last where to

a.person_id is null

?

So your query should like this:

SELECT * 
FROM VW_REQUIRED r
LEFT JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id
WHERE a.person_id is null

Or maybe the "old" way?

SELECT * 
FROM
VW_REQUIRED r,
VW_ACTUAL a
WHERE  r.person_id = a.person_id(+) 
AND r.target_id = a.target_id(+)
AND a.person_id is null  

OTHER TIPS

Maybe your problem came from a bad plan in the non exist query.

could you please show us the plan for this query?

SELECT *
FROM VW_REQUIRED r
WHERE NOT EXISTS ( SELECT 1 FROM VW_ACTUAL a WHERE a.person_id = r.person_id AND a.target_id = r.target_id) 

And try to change the join alorithm ( /+use_nl(a)/ or /+use_hash(a)/). I think it's a nested loop. Maybe you have to put a hash join in this example like this

SELECT *
FROM VW_REQUIRED r
WHERE NOT EXISTS ( SELECT /*+use_hash(a)*/ 1 FROM VW_ACTUAL a WHERE a.person_id = r.person_id AND a.target_id = r.target_id) 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top