Scott Blomquist, Tribal Librarian

Mastering T-SQL subtleties the hard way

Every once in a while, I think I know exactly what the code that I'm writing is doing, and then I discover, often through some catastrophe, that I didn't quite understand all the details.  One particularly instructive (or would that be destructive?) example that comes to mind is a T-SQL mistake that ended up deleting every row in a table instead of just the three or so that I was expecting to delete (feel free to play along on your own SQL Server using the Northwind sample database):

delete from Suppliers
where ContactName
  in
  (
    select ContactName 
      from Shippers
     where ShipperID >= 3
  )

What happened is that ContactName is not a valid column name in the Shippers table, but even inside the parentheses the ContactName column from the Suppliers table is in scope. In fact, just because you mentioned a column that only lives outside the parentheses, SQL Server is happy to treat this as what is called a "correlated subquery", and it'll go to the trouble of doing a cross join on the two tables to give you every possible pairing of rows. Needless to say, this result set includes every ContactName under the sun even after being filtered against the where clause, which means the delete statement has the full authority to drop every row in the Suppliers table.  Ouch!

I really wish that I could ask SQL Server to give me some sort of warning in cases like this, as it seems to me that most people who haven't done much with the 'in' syntax would, like me, not expect SQL Server to even cooperate if the stuff inside the parens doesn't parse when taken alone. But now I know better. And try as I might, I just can't stay upset as I had a backup from earlier in the day, and it sure made for a good puzzle.

Published Thursday, July 01, 2004 12:36 AM by TribalLibrarian

Comments

 

Todd Ostermeier said:

Pretty common mistake, though I've not been bitten that hard yet. Consider qualifying your columns with the table:

delete from Suppliers
where Suppliers.ContactName
in
(
select Shippers.ContactName
from Shippers
where Shippers.ShipperID >= 3
)

This will give you an error, because ContactName is not in Shippers. If you don't want to type out the full table name every time, use an alias for the table.

You've probably seen this syntax already when doing joins, since SQL can't resolve columns that exist in both tables. In that case, you *must* scope your columns, so it's not a bad habit to get into all of the time.
July 1, 2004 1:07 AM
 

Paul Bartlett said:

And table aliases make this approach more bearable:

DELETE FROM Suppliers su
WHERE su.ContactName IN (
SELECT sh.ContactName
FROM Shipper sh
WHERE sh.ShipperID >= 3
)
July 1, 2004 4:02 AM
 

matthew said:

good reason not to use subqueries in deletes:

delete from Suppliers
join Shippers on Shippers.contactname = suppliers.contactname
where ShipperID >= 3
July 1, 2004 5:28 AM
 

Kiliman said:

I agree about not using subqueries.

I always create a SELECT statement that matches the rows I want to delete, then comment out the SELECT and add DELETE.

DELETE Suppliers
-- SELECT *
FROM Suppliers INNER JOIN Shippers ON Suppiers.ContactName = Shppers.ContactName
WHERE Suppliers.ShipperID >= 3

If I'm using Query Analyzer, I always wrap my DELETEs and UPDATEs in BEGIN TRAN/ROLLBACK TRAN blocks. It's a good way to catch mistakes that affect more rows than expected.

Kiliman

July 1, 2004 6:20 AM
 

Jon Galloway said:

I'm with Kiliman on this - I like to SELECT before I DELETE. I sometimes generate separate DELETE scripts (depending on the complexity) from a SELECT statement.
July 1, 2004 9:04 AM
 

TrackBack said:

ouch! or... why best practices are not just for sprocs :)
July 1, 2004 11:28 AM
 

Feed Search Engine - All Fresh Articles And News Are Here said:

November 25, 2007 1:34 PM
New Comments to this post are disabled

This Blog

Syndication

Tags

No tags have been created or used yet.

News

These postings are provided "AS IS" with no warranties, and confer no rights.

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker