Tag hackery

Database Restores – Where’s my Transaction Log Backup?

Developers! DBAs! Has this ever happened to you?

Surprise! It's a database migration error!

You’re chugging along on a Friday night getting ready for your weekend deployment. Your 2 liter of Shasta is ice cold, you have your all Rush mix tape, and you’re wearing tube socks with khakis. Things are looking up. You open up your deployment script. You’re confident because you’ve tested it in the QA environment and everything worked. You press F5 and lean back in your chair, confident that the script is going to fly through all of the changes. Suddenly, there’s an error and you’re choking in surprise on Shasta.

In an ideal world, you could pull out your trusty log backups and do a point in time restore, right? What if you’ve never taken a transaction log backup? What if you only have full database backups? Can you still recover from this situation? The answer, thankfully, is yes.

Let’s break something!

USE ftgu;
GO

-- at midnight, we took our initial back up
BACKUP DATABASE ftgu TO DISK = 'C:\ftgu-1.bak'
GO

-- customer data from the business is inserted
-- more customer data is inserted

-- some kind of migration goes here

-- insert a bad value
INSERT INTO Bins (Shelf, Bin)
VALUES ('B', 9)
GO

SELECT GETDATE();

SELECT * FROM Bins WHERE Shelf = 'B' ORDER BY BinID DESC;
GO

-- wait for a bit
WAITFOR DELAY '00:01:00';
GO

-- do something dumb
DELETE p
FROM Products p
JOIN Bins b ON p.BinID = b.BinID
WHERE b.Shelf = 'B';

DELETE FROM Bins WHERE Shelf = 'B'
GO

SELECT GETDATE();
GO

We have a starting backup, no t-log backups, and we’ve gone and deleted some important data from the production database. How do we get it back? If we restored the database from our first backup we might lose a lot of data. Who knows when the last database backup was taken? Oh, midnight. So, in this case, we’d lose a day of data. Well, bugger. In a panic, we save the state of our broken database.

-- ack!
BACKUP DATABASE ftgu TO DISK = 'C:\ftgu-2.bak';

And then we realize that we also need our transaction log:

-- ah crap, I need to back up my log to get point in time recovery!
BACKUP LOG ftgu
TO DISK = 'C:\ftgu-log-1.trn';
GO

Here’s the kicker – the transaction log has never been backed up. (In my experience, this is all too common.) This database has been running for a week or a year or three years without any kind of transaction log backups. We’re screwed right? I mean, wouldn’t we have to apply all of the transactions from the log to the very first full backup we have? No.

Let’s get started and restore our last good backup. We always have our backup with missing data, just in case we need it for some reason.

-- switch to master (need to make sure nobody else is using that database)
USE master;
GO

-- restore the last full backup with known good data
-- make sure to specify NORECOVERY so we can
-- apply our transaction log backup
RESTORE DATABASE ftgu
FROM DISK = 'C:\ftgu-1.bak'
WITH REPLACE, NORECOVERY;
GO

SQL Server is cunning and records the log sequence number (LSN) from the last full backup (technically it’s the start and end LSN from the last full backup). If we have a log backup that encompasses the relevant LSNs, we’re good to go. Since our transaction logs were never backed up before today, we’re safe.

We’re going to use something called

-- restore the log backup until right before we started
-- this is called "point in time recovery"
RESTORE LOG ftgu
FROM DISK = 'C:\ftgu-log-1.trn'
WITH STOPAT = '2011-02-13 10:03:55.653';

Even though we never took a transaction log backup before today, we’re able to take a backup and recover from what initially seemed like a bad situation.

Shrink, Damn’d Log! Shrink, I Say!

Ever have a database log file grow to epic proportions on the weekend? I have. Ever forget to set up job monitoring on that server? I have. Ever get so pissed off that you decided to write a job to automatically shrink the log files whenever they grow? I have.

T-SQL Tuesday 4: Io, Io, it’s off to disk we go

Fact: the earliest recorded use of cloud computing was ancient Greek porn

Io was a nymph. True story. Apparently, her father was some kind of river god. In modern times that means you’re likely to catch fire. Back in the days when the Greeks were in charge of things being a river god meant that you were somebody (the Greeks thought the earth was a giant brass plate floating a huge river, all of which was created by perverts who lived on top of a mountain). So, apparently Io’s dad was important.

Anyway, it is rumored that Io was attractive. So attractive, in fact, that Zeus, lord of the perverts, saw her taking a bath and got more than a little bit aroused. Zeus then behaved in a way that would end up in a savage beating and restraining order back where I come from – he pestered Io for nookie until her father drove her out of the house – probably because some horny lunatic who could shoot children out of his forehead was bothering his daughter. Io, being a bit strange in the head, relented. Or something. My records aren’t 100% clear seeing as how they’ve been written on pottery. The point is that Zeus turned into a giant cloud and turned Io into a cow (no, your hooves don’t make you look fat).

Somehow Zeus’s wife got involved and there was bondage involving a cow tied to a tree or something. Eventually Io gets turned back into a real live girl and gives birth to Zeus’s son. Which brought about an ethics probe into cross-species cloning.

Disk… disk… oh yeah I already mentioned that the ancient Greeks were clearly insane and thought that the world was a giant metal plate floating on a huge river name Oceanus all of it encased in a hemisphere with clouds and the sun and the moon and stuff painted all over the inside of the hemisphere.

What’s outside of the hemisphere? Shut up, that’s what. It’s turtles all the way down.

There’s a bit of humor thrown into your T-SQL Tuesday

My MacGyver Moment

David Stein started this current blog meme. He passed the buck on to Brent Ozar, who shared a horrifying tale of his time in the trenches as a developer. Brent then thoughtfully pointed at me and demanded that I carry on the blog meme torch.

Blog memes are great. They give me an opportunity to pretend to be inventive and creative, take someone else’s great idea and spin it in a centrifuge to extract the good stuff and then pass the detritus along to someone else to deal with. As a career developer turned developer berating apparatus, I have seen my fair share of cobbled together dung heaps. In fact, I would be proud to have someone call some of my solutions a cobbled together dung heap.

Bad Process? Just Add Software

Many years ago I was asked to put together a simple proof of concept application showing code movement using VSS automation. The phrase “simple proof of concept” should have sent me running. Of course, I’m assuming that the me of 5 years ago had any sense in his head.

I’ll back up a minute and let you see the whole situation. This organization had a complex development lifecycle. Developers were frequently developing several releases into the future in a combination of major and minor releases. Hot fixes would go out in production and when the next major release went out the icon be cornflower blue but the hot fix would be nowhere to be found. The hot fix would be hot fixed into production and the icon would be back to alice blue. The situation was, in short, a nightmare. What makes the nightmare worse was that it was a process nightmare: development practices in this organization dictated that multiple releases be developed simultaneously by separate teams.

Think about complex, line of business, software. Important software. Software that is core to your business. Imagine that three separate teams of developers are actively developing new features and fixes at the same time in three separate source trees. This is exactly what was going on at this organization. My job was to automate the problem away with software.

Bad Software? Just Add More Software

This organization was heavily invested in using Visual Source Safe. For those of you young enough to have worked with modern version control systems (such as a team of scribes working around the clock), let me tell you about VSS. VSS exists entirely on the client computer. VSS’s source control mojo operates through a network share using a combination of black magic and pure luck. Like all things that work with luck, it fails. It is safer to print your source code, shred it, and burn the paper shreds that to keep your code in VSS.

Despite the pain of VSS, VSS was the platform. How do you automate a platform when there is no server-side component? You install a VSS client on a server and you begin automating it. Thankfully VSS supplied an API – a poorly documented COM+ API.

I created a proof of concept application that showed code motion through various stages of development in VSS. The client was happy. The client signed a statement of work. We took their money and strode purposefully towards what appeared to be a set of rotating blades.

As the project unfolded it became very apparent that no amount of software could solve this problem. The problem was process, not software. Valiantly, foolishly, 26ishly, I soldiered forward. I worked 14 hour days. I build a cobbled together GUI that attempted to model a software development process that was beyond complicated. The GUI was cumbersome to use and brittle to code. A small change in one line of code meant that 17 other bugs showed up in various parts of the program. There were easily 100,000 lines of code in what should have been a simple management application. Edge cases lead to other edge cases. After one marathon 24 hour programming session (after four 18 hour days) the GUI tool finally worked. The users could configure a process. A process that potentially didn’t work.

Throwing Worse Code After Bad

The actual automation was a supposed to be easy. The users could apply VSS labels and the automated software would, in theory, pick up the changes within 15 minutes. The automated software would pick up the changes on a regular basis but it frequently exploded in a fiery error when confronted with real world problems. When two teams are actively developing against the same code base, code merges don’t go very well.

The process was intrinsically human but the client wanted automation. Being young and stupid, I didn’t know enough to say no. I cobbled together a working knowledge of text parsing and attempted out outfox the people who wrote the VSS Merge Tool. Armed with a fistful of academic papers, articles, and APIs I attempted to build a better merge tool using existing libraries.

At the end of the day, the tool built a log of errors using the unified diff format (which no human can read), collected the log and emailed a copy of it to the developers who committed the conflicting files. Or, it would have had anyone used the software.

Didn’t You Read the Manual

I actually wrote a software manual. It was actually a good manual. Want to know how I know? I gave the manual to the project manager and had him create instructional videos. They worked.

The project sponsor was promoted before the project was completed. The primary users transferred to another department. The new users were too busy to read the manual and expected the software to work easily and obviously. Who wouldn’t, right?

The GUI tool looked like the directory structure in VSS, but it didn’t reflect the changes in VSS, it simply let the users configure how the tool should behave. Sound confusing? It was.

At the end of the day, I had written a complicated manual for a complex piece of software that actually worked. It was a cobbled together solution built out of several hundred thousand lines of buggy .NET 1.1 code sitting on top of a buggy COM+ API intended to solve a buggy process. It’s a miracle that this software abomination wasn’t fueled by the shattered dreams of orphans.

Silent Rage Would Be Better Than This

I said that was the end of the day. It wasn’t. I got one support call about the product about 3 months after it had been delivered, signed off, and paid for. I went back to the client’s location and sat down with the current team to learn their problems. None of them knew how to use the software. No original team members were around who had been involved in the original project. All of the original documentation had been lost in some kind of bit rot tar pit.

My beautiful, cobbled together, duct tape and baling wire solution would never see the light of day. It may have been ugly, but it worked. There’s nothing like feeling the triumph of having saved the day and then having that day get flushed right down the toilet.

Calling Other MacGyvers

Since Brent Ozar stole my default victim, Matt Nowack, I’m going to have to find more victims. I’m going to point the finger of shame at TJay Belt, Grant Fritchey, and Alex Moore.

How the Hell Did I Get Here?

Paul Randal started this chain post. He tagged Steve Jones who, in turn, tagged Jack Corbett who finally tagged me. I’m pretty sure everyone who nominally makes sense has already been tagged at this point thus leaving Jack to scrape the bottom of the barrel.

Like Jack, I think I could approach this in a number of different ways. I think I’ve already answered the professional route that I took. If I haven’t, it’s probably because my professional route isn’t all that interesting and it’s also all available on LinkedIn. You can, and should, fill in the job changes on my resume with something interesting like “After being attacked by a bear in the janitor’s closet at CareWorks Technologies, Jeremiah decided to take a safer job at HMB (they have no bears on staff as janitors).”

Anyway, you asked for it, you got it: how the hell did I get here?

I’m a Rock and Roll Machine

I love being on stage and in front of people, even though it terrifies the crap out of me. Apparently, I like that adrenaline surge. I’ve been playing guitar since I was 13 years old. When I was 23 I answered an advertisement and auditioned for a band. I got the job after 5 minutes.

Being a musician takes a lot of hard work, dedication, and practice. You work for hours and hours as a group, and hours and hours on your own preparing for a show. At that show, you’re going to walk up on stage and try to steal the attention of a room full of people who would, frankly, rather be doing one of a million other things – playing pool, talking to friends, hitting on that girl across the bar – than listening to you. As musicians, it was our job to get their attention, hold it for an hour, and make sure that they were happy about it. That job gets even harder when you’re in a band that only plays original material.

What seemed really fun – being in a band – turned out to be a lot of work – practicing three nights a week for four hours a night with the band and then practicing even more on my own. I learned a lot about myself – my tolerance for bullshit, how to get attention, and how to act in front of a crowd of people – while I was in the band. I also learned a lot about how to budget scarce resources – money and time – while still getting the job done – getting to the show.

The band eventually fell apart, as most do, but I learned many valuable lessons that I carry with me – time and resource management, performing skills, and how to make an ass of yourself and be okay with it. Most importantly, I learned that passion alone isn’t enough. You have to work for something if you really want to be good at it.

Everything to Everyone

The fact is, I’m not everything to everyone. But for a long time, I thought that I could do it.

Before and while I was in the band, I was married. The band took up a lot of time and it took a lot of time away from my marriage. While being in a band didn’t cause my eventual divorce, I’m sure it contributed to it (I quit the band about a year before my ex-wife and I quit the marriage). I remarried pretty quickly and that marriage ended almost as fast as it started.

Throughout all of this, though, there’s a huge undercurrent – I was trying to make everyone happy. I was trying to be a good husband, musician, developer, friend, son, brother, step-father, and about a million other things. I stretched myself thin and I broke.

These days, I know that I can only be me and that I’m the only person I need to make happy. There’s a reason why I work with SQL Server but I program with Ruby, why I listen to old school hardcore punk but I play a bizarre blend of folk and country, why I devote more time to my friends and family than I have before – these things all make me happy. If it doesn’t make me happy and I don’t need to do it to live, I don’t do it.

Self-Fulfilling Catastrophe

A couple of paragraphs ago I said “I stretched myself thin and I broke.” I really do mean that. During the first divorce, I moved into a tiny house on the ass end of Columbus, stopped paying most of my bills, and ended up living on as little as $20 a week. The funny part, though, is that I always found the cash to go out and party, or to stay in and party. This became a bad habit even once the divorce was done and I should have been back on my feet. Over the next 4 years everything spiralled completely out of control.

I’m pretty sure there were more than a few times I nearly lost my job. I frequently called in “sick” from the crowded patio of a bar at 1:30AM, had my car repossessed, racked up so much debt that people were calling my family members to find out where I was, and I partied seven nights a week. I lost a lot of my friends and damaged most of my remaining friendships irrepairably in the process. Throughout this ordeal, a few of my friends stood by me. They didn’t give up on me despite my ardent attempts to turn myself into a drooling train wreck of a human being.

In June of 2008, I gave it all up. I realized that I was a total train wreck and that everything around me was completely out of control. My career was stagnant. I was sliding backwards as a person. I wasn’t meeting any of my goals for myself because I was too busy slowly killing myself.

I stopped drinking. I got the help I needed and I began the long, painful, process of pulling myself up by my shoelaces. I dried out.

By August, I had completed the SQL Server 2005 MCITP: Database Developer certification. I started the Columbus chapter of PASS in October. I started paying back all of my bad debt (only a few months left to go). Nine months after I quit drinking, I quit my one to two pack a day smoking habit (sorry about that one, Mom and Dad) – I never would have thought I could end my 13 year addiction to nicotine.

I have a great relationship with my family and friends now, it’s better than anything I could ever hope for.

I learned a lot of things from this.

  1. I can be horribly selfish.
  2. There’s nothing better than not being that selfish.
  3. I can do damn near anything I want to do if I put my mind to it.
  4. There are some things in life that are so important you can’t afford to overlook them.

Afterward

This summer, one of my friends (someone who met me at my lowest and stuck by me through everything) is giving me the greatest honor I could ever hope for: on June 19th I’ll be officiating his wedding. Like a lot of people, I wouldn’t change a thing about my life.

Getting the ISO Week and Year

Long story short: I need the ISO Week. We need to be able to make sure that when we’re reporting, we’re pulling data for the full week (which happens to coincide with ISO Week at my employer).

The problem with using an existing function is that the existing functions just return the week and not the full year + week combination. I used the function from the Less Than Dot wiki as a starting point and added my own code to create the full YYYYWW string to give me an ISO Week. This isn’t actually in the ISO standard format for reporting the ISO week, but I also don’t care so long as I can use the ISOWeek for effective querying.

Enjoy

ALTER FUNCTION ISOweek (@DATE DATETIME)
RETURNS VARCHAR(6)
AS
  BEGIN
    DECLARE @ISOweek INT,
      @year INT,
      @rVal VARCHAR(6) ;

    SET @ISOweek = DATEPART(wk, @DATE) + 1 - DATEPART(wk, CAST(DATEPART(yyyy, @DATE) AS CHAR(4)) + '0104') ;
    SET @year = DATEPART(yyyy, @DATE) ;

    SET @rVal = CAST(@year AS VARCHAR(4)) + RIGHT('00' + CAST(@ISOWeek AS VARCHAR(2)), 2)

    --Special cases: Jan 1-3 may belong to the previous year
    IF (@ISOweek = 0)
      BEGIN
        SET @rVal = dbo.ISOweek(CAST(DATEPART(yyyy, @DATE) - 1 AS CHAR(4))
                                   + '12'
                                   + CAST(24 + DATEPART(DAY, @DATE) AS CHAR(2)))
                                   + 1 ;
      END

    --Special case: Dec 29-31 may belong to the next year
    IF (
        (DATEPART(mm, @DATE) = 12)
        AND ((DATEPART(dd, @DATE) - DATEPART(dw, @DATE)) >= 28)
       )
      BEGIN
        SET @ISOweek = 1 ;
        SET @year = DATEPART(yyyy, @DATE) + 1 ;

        SET @rVal = CAST(DATEPART(yyyy, @DATE) + 1 AS VARCHAR(4)) + '01'
      END

    RETURN @rVal ;
  END
GO

This lets me do some snazzy reporting hackery like this:

SELECT  MIN(c2.[Date]) AS StartDate,
        MAX(c2.[Date]) AS EndDate
FROM    dbo.Calendar AS c
        INNER JOIN dbo.Calendar AS c2 ON c.ISOWeek = c2.ISOWeek
WHERE   c.[Date] = '20091229' ;

/*
StartDate               EndDate
----------------------- -----------------------
2009-12-27 00:00:00.000 2010-01-02 00:00:00.000
*/

Which is then used to feed report parameters.

text TEXT teXt?

The Problem

A reader ran into a problem on an old post about Automating T-SQL Testing. Thankfully, rather than decide that I’m some useless crank on the internet, this reader took the time to write in to me and tell me about the problem. But, he went one step further: he documented step by step his problem!

The Process

I fired up SSMS and verified his problem (using the thoughtfully provided steps to duplicate). Sure enough, my code didn’t work. Which is odd because I’ve used this very same code to test stored procedures.

At this point, I calmly stood up, walked away from my computer, opened the window, and swore loudly. I then closed the window, walked back to the computer, and sat down. After numerous permutations, I finally searched google in exasperation only to find out it was a capitalization issue (read all about it over here).

Where was this coming from?

The Actual Problem

The GeSHi JavaScript syntax highlighting library was capitalizing the word ‘text’ because it is a datatype in SQL Server. Unfortunately, the text() XML method is lowercase. So, in this isolated incident, my syntax highlighting was causing me problems. The solution was to remove syntax highlighting from that portion of my post and inform the reader of the problem.

What to do?

Well, you need to test things. In this case, though, it was something that I couldn’t effectively test. The developers of the GeSHi plug-in should probably be testing these things (and I’ll be tracking down their info to send them a bug report). The key here is, I guess, to be very very careful. Really I just wanted to share this bug so others don’t get caught by it.

A Case of Shifting Identity

- Or -
A Brief Foray Into the Realm of IDENTITY Columns

We all know, or should know, when we insert into a table using using SET IDENTITY_INSERT, that the identity value will be reset to the next available value in the sequence. Meaning that the identity value will either be the next value that it was supposed to be (if we filled a gap in the sequence) or the next value larger than the row we’ve just inserted (so if we inserted 100, the next identity will be 101). Right? Well, sort of. Books Online, somewhat confusingly, states that “If the value inserted is larger than the current identity value for the table, SQL Server automatically uses the new inserted value as the current identity value.” Define larger.

In this case, larger actually does mean the next available value in the sequence. Check it:

IF OBJECT_ID('tempdb..#t') IS NOT NULL
  DROP TABLE #t;

CREATE TABLE #t (
  id INT IDENTITY(1,1),
  val VARCHAR(10)
);

INSERT INTO #t VALUES ('a');
INSERT INTO #t VALUES ('b');
INSERT INTO #t VALUES ('c');
INSERT INTO #t VALUES ('d');
INSERT INTO #t VALUES ('e');
INSERT INTO #t VALUES ('f');

SET IDENTITY_INSERT #t ON;

INSERT INTO #t (id, val) VALUES (-1, 'g');

SET IDENTITY_INSERT #t OFF;

INSERT INTO #t VALUES ('h');

SELECT * FROM #t;

“ZOMG, he’s so smart!” I know, that’s totally what you’re saying, right? Well, by using a straightforward example, I’ve gone ahead and proved nothing apart from showing you that I know how to use SET IDENTITY_INSERT and copy/paste.

So, what happens if you want to use a negative identity seed? Technically the numbers are getting smaller, not larger, right?

IF OBJECT_ID('tempdb..#t') IS NOT NULL
  DROP TABLE #t;

CREATE TABLE #t (
  id INT IDENTITY(-1,-1),
  val VARCHAR(10)
);

INSERT INTO #t VALUES ('a');
INSERT INTO #t VALUES ('b');
INSERT INTO #t VALUES ('c');
INSERT INTO #t VALUES ('d');
INSERT INTO #t VALUES ('e');
INSERT INTO #t VALUES ('f');

SET IDENTITY_INSERT #t ON;

INSERT INTO #t (id, val) VALUES (1, 'g');

SET IDENTITY_INSERT #t OFF;

INSERT INTO #t VALUES ('h');

SELECT * FROM #t;

Witchcraft! I made the numbers decrease instead of increase! But then I used good magic to put a positive number in there and then the witches corrupted the sequence and the numbers got smaller again!

Sensibly speaking, though: it works. The identity resumes the normal sequence and order is again restored in the world. Had I inserted -10 instead of 1, the next value inserted into the id column would have been -11. Which makes sense.

Why the linguistic confusion on the part of Books Online? I don’t know. I would hazard a guess and say that it was implied that they meant “larger in relation to the IDENTITY seed that you have set up” (and probably muttered the words “you pedantic toad” under their breath). But rather than explicitly go out on a limb and define such a thing in the documentation for SET IDENTITY_INSERT, they assumed that people would infer the meaning. You know, they assumed that people would make sense.

Why would you ever want to do something silly like this? I have my reasons, but I’m sharing this as a test of the application functionality that I needed and a useful demonstration of how IDENTITY works.

Rounding to the nearest 0.5

Yesterday I encountered a fun little problem where I had to round to the nearest 0.5. Admittedly, I solved the problem in C#, but the solution is the same no matter what language you are in.

So, here’s our starting data set:

IF OBJECT_ID('tempdb..#ratings') IS NOT NULL
  DROP TABLE #ratings ;

CREATE TABLE #ratings ( rating INT ) ;

INSERT  INTO #ratings
        SELECT  1
        UNION ALL
        SELECT  3
        UNION ALL
        SELECT  2
        UNION ALL
        SELECT  7
        UNION ALL
        SELECT  9
        UNION ALL
        SELECT  8
        UNION ALL
        SELECT  3 ;

And, of course, the average is really easy to get as well:

SELECT  AVG(CAST(rating AS DECIMAL)) -- Use decimal so we don't lose precision
FROM    #ratings ;

But that average really doesn’t do anybody any good since it’s nice and precise. How are we going to get to this number? Well, funnily enough if we multiple our average by 2 and round it to the nearest whole number and divide it by 2 (still with me?) we’ll end up with a number that’s rounded to the nearest 0.5.

SELECT  ROUND(AVG(CAST(rating AS DECIMAL)) * 2, 0) / 2
FROM    #ratings ;

It’s also possible to change your rounding precision simply by change the number you multiply and divide by… Want everything in thirds? Just use 3 instead of 2. Magic! You can use this in your rating algorithms to give you half or quarter stars or whatever the heck you want. Happy rounding!

Links for the Week of 2009.06.12

SQL Server

Denis Gobo provides a double dose of dynamic SQL hotness via Changing exec to sp_executesql doesn’t provide any benefit if you are not using parameters correctly and Avoid Conversions In Execution Plans By Using sp_executesql Instead of Exec. Following his tips in here will net you some considerable benefits in how your dynamic SQL behaves and performs in your production applications. The best part is that he provides a huge amount of example code to back up everything he’s saying.

Free SQL Server tools that might make your life a little easier Mladen Prajdic maintains an amazing list of free tools to help data folks get their job done. SSMS Tools Pack (which Mladen created and maintains) is incredibly helpful. I suspect that if he finds these other tools useful, they’ll probably save you many many hours over the years to come.

SQL Server Troubleshooting Tips and Tricks This isn’t a blog post so much as it is really important to keep around. It’s just a list of tips and tricks to keep things easy and painless with SSMS. If you didn’t check out the pain of the week webcast with Michelle Ufford and Brent Ozar on SSMS, you should do so when you get the chance, it’s well worth it!

More SQL Server Training Videos Training. Free. Videos. I’m in there, that’s really all the reason you need to visit that link.

BONUSTen SQL Server Functions That You Have Ignored Until Now Bonus link from Denis Gobo, once again, covering some great, little known, SQL Server functions.

Development

Unit Testing is Not a “Figure It Out Later” Read this. Read this now. Do it. We’ll still be here. Stop what you’re doing and read this. I cannot stress how important it is for you to read this, even us data folks. If I could put a blink tag around this paragraph, I would. (Thanks to Stephen Wright for finding this link.)

Defensive Programming – Assumptions Must be Guaranteed or Tested Aaron Alton blogs about the importance of defensive programming. While this is specifically from a SQL Server standpoint, it’s important no matter which language you’re using. In fact, Aaron proves an important point – you can put any development practice to use in any language.

Stuff & Things

Improving your intranet – keep it sustainable using kaizen – Patrick Walsh talks about using kaizen to produce evolutionary improvements to an intranet. You could do this with anything, really, but it’s a good read either way you look at it.

I cheated and stole the last two links from Lifehacker, but they’re just too good to not share.

CopyPasteCharacter.com Makes Special Characters Easy to Paste Yup, easy. Nice. Easy mode: engage.

LiberKey Installs 200+ Portable Applications I have too many thumbdrives laying around the house. Now I have a use for them.

This site is protected with Urban Giraffe's plugin 'HTML Purified' and Edward Z. Yang's Powered by HTML Purifier. 401 items have been purified.