views:

147

answers:

7

I have an Oracle SQL query as part of a stored proc:

DELETE FROM item i 
 WHERE NOT EXISTS (SELECT 1 FROM item_queue q WHERE q.n=i.n) 
 AND NOT EXISTS (SELECT 1 FROM tool_queue t WHERE t.n=i.n);

A bit about the tables:

  • item contains about 10k rows with an index on the n column
  • item_queue contains about 1mil rows also with index on n column
  • tool_queue contains about 5mil rows indexed as well

I am wondering if the query/subqueries can be optimized somehow to make them run faster, I thought that deletes were generally fairly fast

A: 

try to use SELECT instead of delete to see is DELETE operation real bottleneck

Andrey
+4  A: 

Turn your delete into a select, then you can check and optimize the query part.

Otherwise note - deletes are not the fastest thing around. LOTS of things happen on a delete.

OTOH I seriously think.... the problem is the two subqueries. What does the query plan look like?

TomTom
+1  A: 

Try avoid Subselect in you quires and use INNER JOIN instead

adopilot
Except an INNER JOIN won't work. If you inner join between `item` and `item_queue` and `tool_queue` you will get a subset of the `item`s that you do _not_ want to delete.
Shannon Severance
Isn't the inner join more expensive than select statements? Please correct me if i am wrong.
sprasad12
@Shannon Severance Golden rule "Two TIME select One time delete"@sprasad12 Let someone whit more experience correct me, But I as I know, things as Subselect and Functions are much slower then JOINS in queries
adopilot
With the proper constraints in place the optimizer will just change it for you. Without them it may or may not be equivalent.
Stephanie Page
A: 

While it may not be any faster it would be easier to read if you did:

DELETE FROM item i 
WHERE n NOT IN (SELECT n FROM item_queue)
AND n NOT IN (SELECT n FROM tool_queue)
Shannon Severance
+3  A: 

Try something like:

 DELETE FROM item WHERE n NOT IN 
     (SELECT i.n FROM item i INNER JOIN item_queue q ON i.n = q.n
      UNION SELECT i.n FROM item i INNER JOIN tool_queue t ON i.n = t.n)

Your correlated sub-queries are running 10K times each in your example. This technique will run two INNER JOIN queries to get the list of "n"s to delete.

You may need to fiddle the SQL a bit; I'm not familiar with the Oracle dialect.

Larry Lustig
The syntax should be fine for Oracle, but I think you need to use `or` instead of `and`, since the original checks that **both do not exist**. Also, using **excplicit joins** would make it more readable.
Peter Lang
Thank you Peter, you are correct. I edited the answer to separate out the two queries and UNION the result sets together which avoids the need to get into a messy multi-table JOIN; and I reformatted to use the INNER JOIN syntax (although for an old guy like me, they're equally readable).
Larry Lustig
@Larry Lustig: I agree they are equally readable in that case, but I doubt someone can be old enough to find queries using dozens of implicitly joined tables mixed with some *real* conditions equally readable ;-)
Peter Lang
I dunno, Peter. I'm pretty old. . .
Larry Lustig
Depends on what you're used to... I can't read explicit joins worth sh!t. I want to see all the tables in one place. Not scattered about.The real issue is a design issue. The fact they separated the tool_q from the item_q was probably a mistake. It should probably be a single queue table with a differentiating column if necessary. I bet that they have code like this all over...
Stephanie Page
Also I would also suggest that a UNION ALL may be appropriate based on table sizes.
Stephanie Page
@Larry: incorrect. The original had `AND NOT EXISTS` but yours will delete any that are in `item_queue` OR `tool_queue`.
Jeffrey Kemp
You could use `INTERSECT`.
Jeffrey Kemp
@Jeffrey Kemp: Incorrect. Larry used `NOT IN`, so it will delete all that are neither in `item_queue` nor in `tool_queue` (the same way the original query does).
Peter Lang
My apologies - I was wrong, thanks Peter.
Jeffrey Kemp
+1  A: 

Make sure you don't have a constraint on a large table that references your item table. That can be a real slowdown in the case of deletes.

aehiilrs
+1  A: 

You can't really get a good answer for this without doing additional work.

After the SQL statement iteself, the most importasnt thing is that the statistics for the objects (tables and indexes in this case) are representative.

Then you really need to look at the access path that oracle chooses - many ways to do this.

Try

EXPLAIN PLAN SET STATEMENT_IS = 'SQL01' FOR
DELETE FROM item i 
 WHERE NOT EXISTS (SELECT 1 FROM item_queue q WHERE q.n=i.n) 
 AND NOT EXISTS (SELECT 1 FROM tool_queue t WHERE t.n=i.n);

Then

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

Maybe post the result here.

As you try different things - such as re-writing the query, modifying indexes etc, you will notice that the access path changes.

This is a rather complex area - and you will need to study / practice.

Deletes are slow for a number of reasons, but a big factor is the maintenance of the indexes on the table. However, in your case, you say there are only 10k rows which is pretty small. (BTW you did not give timings here. Is it taking 1, 10 or 100 seconds at the moment? and what do you want to achieve? ) So I would be focusing on the access path through the larger tables.

My first approach would might be:

DELETE FROM item i 
 WHERE NOT EXISTS 
(SELECT NULL
 FROM item_queue q,
      tool_queue g
  where q.key = g.key      -- if the tables are related
    AND q.n=i.n) ;

But like I say there are many factors here.

pj