Using A SQL JOIN In A SQL DELETE Statement (Thanks Pinal Dave!)

Posted September 4, 2007 at 2:43 PM

Tags: SQL

Earlier today, I posted about how John Eric dropped a bomb shell on me, demonstrating that you could run a SQL JOIN statement inside of a SQL UPDATE statement. Well, after reading my blog post, Pinal Dave of The SQL Authority followed up with another revelation - you can do the same thing inside of a SQL DELETE statement. The syntax for this is a bit strange, but perhaps equally powerful.

Taking my previous example with the @boy, @girl, and @relationship tables, I am now updating it such that we are going to delete all boys who have not had the skills to date Winona Ryder. Now, I am not saying that I would necessarily perform the SQL DELETE using this exact methodology, but certainly, it is fun to explore this SQL flexibility.

 Launch code in new window » Download code as text file »

  • <cfquery name="qUpdateTest" datasource="#REQUEST.DSN.Source#">
  • <!--- Declare in-memory data tables. --->
  • DECLARE
  • @boy TABLE
  • (
  • id INT,
  • name VARCHAR( 30 ),
  • is_stud TINYINT
  • )
  • ;
  •  
  • DECLARE
  • @girl TABLE
  • (
  • id INT,
  • name VARCHAR( 30 )
  • )
  • ;
  •  
  • DECLARE
  • @relationship TABLE
  • (
  • boy_id INT,
  • girl_id INT,
  • date_started DATETIME,
  • date_ended DATETIME
  • )
  • ;
  •  
  •  
  • <!---
  • Populate the boy table with some information.
  • Notice that as I populate the IS_STUD column, all
  • the values are going to be ZERO (meaning that these
  • dudes are not very studly). This will be updated
  • based on the relationship JOIN.
  • --->
  • INSERT INTO @boy
  • (
  • id,
  • name,
  • is_stud
  • )(
  • SELECT 1, 'Ben', 0 UNION ALL
  • SELECT 2, 'Arnold', 0 UNION ALL
  • SELECT 3, 'Vincent', 0
  • );
  •  
  •  
  • <!--- Populate the girl table with some information. --->
  • INSERT INTO @girl
  • (
  • id,
  • name
  • )(
  • SELECT 1, 'Maria Bello' UNION ALL
  • SELECT 2, 'Christina Cox' UNION ALL
  • SELECT 3, 'Winona Ryder'
  • );
  •  
  •  
  • <!--- Populate the relationship table. --->
  • INSERT INTO @relationship
  • (
  • boy_id,
  • girl_id,
  • date_started,
  • date_ended
  • )(
  • SELECT 1, 1, '2007/01/01', NULL UNION ALL
  • SELECT 1, 3, '2004/09/15', '2005/06/15' UNION ALL
  • SELECT 2, 1, '2006/05/14', '2006/05/23'
  • );
  •  
  •  
  • <!---
  • DELETE from the in-memory table. Here, we are going to
  • join the boy, girl, and relationship table to see if
  • any of the boys have NOT been studly enough to date
  • Winona Ryder. We are only interested in keeping boys
  • who have been in this sort of elite relationship.
  •  
  • NOTE: Maria Bello would quite clearly be a studlier
  • conquest, but I am trying to keep in line with my
  • previous UPDATE demo.
  • --->
  • DELETE
  • b
  • FROM
  • @boy b
  • LEFT OUTER JOIN
  • (
  • @relationship r
  • INNER JOIN
  • @girl g
  • ON
  • (
  • r.girl_id = g.id
  • AND
  • g.name = 'Winona Ryder'
  • )
  • )
  • ON
  • b.id = r.boy_id
  • WHERE
  • g.id IS NULL
  • ;
  •  
  •  
  • <!---
  • To see if the delete has taken place, let's grab
  • the records from the boy table; we should now ONLY
  • have boys who have dated Winona Ryder.
  • --->
  • SELECT
  • id,
  • name,
  • is_stud
  • FROM
  • @boy
  • ;
  • </cfquery>
  •  
  •  
  • <!--- Dump out the updated record set. --->
  • <cfdump
  • var="#qUpdateTest#"
  • label="Delete-Updated BOY Table"
  • />

This code has a few cool things in it (in my opinion). For starters, we are demonstrating the whole point of this blog post - running the JOIN inside of the DELETE statement. Here we are using both an INNER JOIN and a LEFT OUTER JOIN. But, we are also performing a LEFT OUTER JOIN to the result of an INNER JOIN of two different tables; that in and of itself is a pretty nifty SQL ability. But anyway, take a look at the final syntax there - we are deleting a table from a table; a little strange, but I guess this is one of those things that you just need to get comfortable with.

Anyway, running the above code, we get the following CFDump output:


 
 
 

 
Using SQL JOIN Inside Of A SQL DELETE Statement  
 
 
 

Notice that now the only record left in the @boy table is Ben - the only one who had a relationship with Winona Ryder. Pretty nifty stuff.

Thanks Pinal Dave!

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page





Reader Comments

Sep 4, 2007 at 3:22 PM // reply »
10 Comments

Ben,

Thanks for posting this article. I glad that you find it useful.
I think my blog reader will find this details very useful as I never wrote about this on my own blog.

Tomorrow I will write down summary and point to this two wonderful (and well explained) post you have. I like your examples.

Regards,
Pinal


Sep 4, 2007 at 3:29 PM // reply »
5,406 Comments

My pleasure. Thanks for the tips.


Kevin
Sep 7, 2007 at 5:07 PM // reply »
1 Comments

I usually like to use a subquery for delete's as they are simpler and have less room for error. An example would be:

DELETE FROM table
WHERE id IN (SELECT id FROM anothertable WHERE this = 'that')


Sep 7, 2007 at 5:34 PM // reply »
5,406 Comments

@Kevin,

That's definitely the way that I roll most of the time. However (and I don't know if this applies to DELETE statements), when I have used sub-queries with UPDATE statements, I could never alias the table name (syntax error). Using this FROM methodology would allow me to do so and would actually give me more power in my updates.

But, true, more power comes with more complexity. Use the best tool for the situation.


SQL Beginner
Nov 14, 2008 at 8:34 AM // reply »
1 Comments

Thanks for the tip! Exactly what I needed for my instead of delete trigger!


hfrmobile
Apr 2, 2009 at 7:39 AM // reply »
2 Comments

A strange sample ...

Since there aren't foreign key constraints the bug in the sample wasn't seen by nobody ... ;-)

"Arnold" (2) and "Vincent" (3) where deleted but in the relation table still contains an entry with boy_id = 2 ...

I was looking for another but similar sample. I need to purge old date from a huge database.

I need to delete old contracts and all related rows in other tables

e.g.
Contract
- FromParticipant
- ToParticipant
- ContractItem
- TransportPlan
- etc.

Not all contracts having a TransportPlan and not always the From/ToParticipant should be deleted (only if they aren't used somewhere else) etc.


BabyMilo
May 25, 2009 at 4:09 AM // reply »
3 Comments

Hi,if a cell contain aa;bb;cc, how am i going to write a statement to delete aa only?
Thanks!


hfrmobile
May 25, 2009 at 8:43 AM // reply »
2 Comments

@BabyMilo: In that case you'll use an UPDATE command ... (of course you have to "calculate" the new values before using string functions)


May 25, 2009 at 8:54 AM // reply »
5,406 Comments

@BabyMilo,

Yeah, you have to do an update:

UPDATE [table]
SET [column] = 'bb;cc'
WHERE [column] = 'aa;bb;cc'


BabyMilo
May 27, 2009 at 2:13 AM // reply »
3 Comments

What if i want to delete aa only without update all the records? there are plenty of records that contains aa.


BabyMilo
May 27, 2009 at 2:13 AM // reply »
3 Comments

What if i want to delete aa only without update all the records? there are plenty of records that contains aa.


May 27, 2009 at 8:26 AM // reply »
5,406 Comments

@BabyMilo,

It won't update all records that contain "aa"; it will only update records that have exactly "aa;bb;cc". That is what the WHERE clause is for.


Amit Mohod
Jun 23, 2009 at 4:46 AM // reply »
1 Comments

@BabyMilo,
U can write as:

update SomeTable
set SomeColumn = 'xxyyzz' where SomeColumn like '%abc%'


Post Comment  |  Ask Ben

Recent Blog Comments
Jason Fisher
Jul 4, 2009 at 4:35 PM
Adobe Announces That HomeSite Is Officially Dead
I'm with Mark and Tim: still the best IDE for CF, especially for those of us with years of customization which can so easily be moved from machine to machine. I just have trouble making the Eclipse ... read »
Secret Admirer
Jul 4, 2009 at 12:23 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 2 / Week 3
My Poor Dreamboat :( I feel so sad when I know you are hurting. I hope you feel better soon. ... read »
Jul 4, 2009 at 9:42 AM
FLV 404 Error On Windows 2003 Server
I bookmarked this page. Thanks for given this great post.... ... read »
Jul 4, 2009 at 4:00 AM
Terms Of Service / Privacy Policy Document Generator
thanks ben, I'm not a big fan of contracts so to find your no no-nesense ToS generator has helped me no end. all the best matt ... read »
Justice
Jul 3, 2009 at 11:10 PM
Create A Running Average Without Storing Individual Values
@Ben, I think you're going about this the wrong way. You're trying to use complicated techniques when there is a simple and beautiful technique readily available (a la Gary Funk's comment). Instead ... read »
Bob
Jul 3, 2009 at 9:19 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
a good technical explanation http://crossfitphoenix.typepad.com/crossfit_phoenix_forging_/the-overhead-squat.html ... read »
Jul 3, 2009 at 9:03 PM
Create A Running Average Without Storing Individual Values
If I wanted to do this and only carry two numbers, I'd keep track of the sum and N. Then you are pretty much accurate all the time. average = (sum + new_number) / (N + 1) But all this was in a for ... read »
Roland Collins
Jul 3, 2009 at 8:58 PM
Create A Running Average Without Storing Individual Values
@Martin - not just floating point though. Depending on what langauge you're working in, decimals can cause just as many headaches if they're not precise enough. But again, for most applications, th ... read »