Getting mails from a Developer that start like this almost always leads to awesome. This turned out to be one of the times when it wasn’t as awesome as it could have been, but did give me the opportunity to spread some knowledge (which I don’t get to do very often, because, well, I’m not that smart).
This situation was the old, “oops, WHERE clauses are a good idea with DELETE statements.” The good news is that this was in Development, so it wasn’t a giant fire. Although I didn’t see the message right when it came in, I did see it in time to get to it before that night’s backup ran (we just keep one backup file in Dev and overwrite it every evening). I probably could have pulled from Production or Test instead of restoring a 140+ gig DB for a 299 row table, but we’ve got the space, more IO than God, and it was a Friday night where nothing else was going on out of the ordinary. Table restored, life goes on.
Actually, there were a couple points that I was able to make with this situation.
First: Tell your DBA when things go bad!
In our situation, with the backup file getting overwritten every night, if a Developer makes a mistake like this, they have to let us know before 8:00 the day of in order for us to be able ion do anything about it. The guys/gals have to first realize something bad happened, and then get to us right then in order to recover. If they sit on it until the next day, it is too late.
Second: BEGIN TRAN is your best friend.
When running DML, manually start and end transactions. Sure, SQL Server has the nice, easy implicit transactions that you don’t have to worry about, but those can become your worst enemy very easily. All it takes is either missed highlighting before mashing F5 or an unfortunately-placed closing paren.
BEGIN TRAN? (skip this paragraph if you already know) By default, SSMS uses implicit transactions. This means that even though you don’t type it out, when you run statements, SSMS begins a transaction, runs your stuff, and then commits it. By manually starting a transaction with BEGIN TRAN in front of your UPDATE, DELTE, or whatever, you retain control of this instead of letting the UI do it for you. This means you can run your statement(s), check the results, and then COMMIT or ROLLBACK yourself. In short, this is manual transaction control.
This one takes some diligence, because it’s easy to be complacent. I’m doing a simple little UPDATE statement, I didn’t make any mistakes, everything will be fine. Of course you think that—you wouldn’t run any statements that you didn’t think were right, would you? This is why you have to tell yourself to type BEGIN TRAN every time. It only takes once to really ruin your day.
OK, Third: COMMIT TRAN until it throws an error
This is another tip that I learned from our senior DBA on probably my first or second day on the job. Basically, when you commit your user transaction, keep trying to commit it until SSMS reports an error (trying to commit a transaction when there isn’t one open). Why? Glad you asked!
Create a table & put a couple rows of data in it:
CREATE TABLE TransTest (
ID INT IDENTITY(1,1),
Name VARCHAR(20) NOT NULL
)
INSERT INTO TransTest
SELECT 'Smythee'
INSERT INTO TransTest
SELECT 'Bob'
Next, say you want to delete Bob from the table. Bob was never any fun anyway, was he? Because you’re heeding the above advice, you are going to wrap this simple one-row delete in a Transaction. You run the following:
BEGIN TRAN
DELETE
FROM TranTest
WHERE Namee = 'Bob'
Whoops, you fat-fingered the column name and didn’t notice until you ran it, and it threw an error.
Fix it and run it again:
BEGIN TRAN
DELETE
FROM TranTest
WHERE Name = 'Bob'
This runs OK, you double-check the contents of the table, everything looks fine.
Next step is to run COMMIT TRAN. That runs without error, and you go on your merry way.
But, there’s a problem: Select @@trancount and see what you get. You should see one transaction still open. Why is that?
When the first statement was run, a transaction was opened. Even though the statement itself bombed because of the bogus column name, that transaction is still there. When you fix it, if you run BEGIN TRAN again, you will now have a nested, second transaction. Running a single COMMIT will commit your changes, yes, but it still leaves one transaction open. Because that transaction still has locks, it will block other statements looking to operate in the TranTest table.
Moral of the story? Mash F5 on COMMIT TRAN until SSMS throws an error.
What was I talking about again?
Oh right, our poor developer.
In the mail I sent back to him, I commended him for being smart about letting us know right away when a mistake was made, as it allowed us to actually get the data back (or mostly so). I also recommended manual transactions, because they can save your tail.
I don’t know if he’ll take the advice to heart, but he at least has the tools available to him now if he wants to use them.