[n]varchar truncation

I was very happy with the response to my SQL Saturday session in Albuquerque. To those who attended and provided me with feedback, thank you.

One of the items I covered led to this tweet from @coffegrl:

thrilled! I was just able to use something i learned at #sqlsatabq. that's ROI for you :)

Which just made my day yesterday. Anyway, here’s what she was talking about. Have a look at this script:

SELECT @stmt = N'foo' + REPLICATE(N'A', 4000) + N'bar';
SELECT LEN(@stmt), LEFT(@stmt, 5), RIGHT(@stmt, 5);

What happens when you run it? Ideally, the results should be 4006, ‘fooAA’ and ‘AAbar’. But that’s not the case. Even though @stmt is NVARCHAR(MAX), the four components of the string concatenation (‘foo’, ‘A’, the results of REPLICATE, and ‘bar’) are not. As a result, the value that is constructed is, in this case, an NVARCHAR(4000), which is then written to @stmt.

Here’s one way to fix it:

SELECT @stmt = N'foo' + REPLICATE(CAST(N'A' AS NVARCHAR(MAX)), 4000) + N'bar';
SELECT LEN(@stmt), LEFT(@stmt, 5), RIGHT(@stmt, 5);

And another:

SELECT @stmt = N'foo' + CAST(REPLICATE(N'A', 4000) AS NVARCHAR(MAX)) + N'bar';
SELECT LEN(@stmt), LEFT(@stmt, 5), RIGHT(@stmt, 5);

And another:

SELECT @stmt = CAST(N'foo' AS NVARCHAR(MAX)) + REPLICATE(N'A', 4000) + N'bar';
SELECT LEN(@stmt), LEFT(@stmt, 5), RIGHT(@stmt, 5);

And the same is true for VARCHAR (assuming you replace the 4000’s with 8000’s). So, the lesson is, when concatenating large strings in SQL be careful that it doesn’t trunca

And So We Meet Again

This is not a technical topic, but one I find myself very passionate about. An article today at NPR brought it to the forefront. As my organization matures, like every organization, rampant movement is slowly replaced by more controlled but slower movement. Notice I used the word “movement”. As Hemingway said, “Never mistake motion for action.” What maturity in an organization often brings is a better ratio of action to motion. We do a better job of working on only those things that really matter, not going off on adventures that may never make their way to production. Or developing with a ready, fire, aim approach. But it also wraps that action in more red tape. It’s a trade-off that every organization encounters.

Anyway, meetings are a highly visible part of that red tape. And the article covers the topic pretty well. I just have one thing to add. It’s a little math that I’ve always kept in the back of my mind at every meeting. Here’s the formula:

Px = T * W

Or, Productivity Lost = Time of the Meeting X the number of Workers at the meeting.

If you are running a meeting for 6 knowledge workers for 2 hours, assume that you just lost 12 hours of productivity. Was that meeting worth those 12 hours? It might have been. But that’s the trade-off. Remember, everyone’s time is valuable.

So, if you have any control over meetings at your organization, please keep that formula in mind. I always do.

Albuquerque SQL Saturday

I’m excited to announce that I’ll be presenting “Fast and Furious Dynamic SQL” at SQL Saturday #358 in Albuquerque on February 7th, 2015. This will be my first time as a PASS presenter, so please save your best heckling and rotten vegetables for the occasion.

The session will cover various tips and tricks that are generally related to running sp_executesql from within a stored procedure. It will focus on performance, security, and maintainability.

Viewing the Plan Cache

I’m working on a script and wanted to know how SQL was caching the query plan. So I wrote this little snippet. The only real value I’m adding here is getting the XQuery correct to pull out the SQL itself.

-- Run your query here
        qp.query_plan.value(N'(//@StatementText)[1]', N'NVARCHAR(MAX)')
    FROM sys.dm_exec_cached_plans AS cp
        CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp;

Of course, I don’t recommend that DBCC in a production environment. But in a dev environment, it makes things easy.

Robinson’s Law

I coined this a while back. I just came across it again, and thought I’d share it. How many of us know this to be true?

“An otherwise inexplicable bug will become easily apparent to the programmer who caused the bug and has subsequently been searching in vain for the bug only and immediately after he announces to his peers that he cannot find the bug, admits defeat, and asks for help.” – Robinson’s Law

Pages Used

One of the items in my toolbox is this simple query that returns the number of pages used by different tables in the database. The nice thing is that it gives one simple number that includes indexes, lob data – basically the whole table.

  FROM sys.dm_db_partition_stats AS ddps
    INNER JOIN sys.objects AS o
      ON ddps.object_id = o.object_id
    INNER JOIN sys.schemas AS s
      ON o.schema_id = s.schema_id
  GROUP BY s.name, o.name
  ORDER BY SUM(ddps.used_page_count) DESC;

I just used this on a database today to find that there are some leftover tables from a migration a while back that are eating up a considerable amount of space. Time to run some DROP TABLE scripts!

Data Overflow

Up until a couple of weeks ago, my impression of Stack Overflow was just simply as a site that showed up on my Google search results when investigating coding issues. Sometimes I found the answer I needed there, sometimes not. And then, while in one of the sessions at PASS Summit 14 – was it one of Kendra Little‘s? – the presenter mentioned that Stack Exchange makes their data available as an anonymized dump. You can find it at https://archive.org/details/stackexchange, hosted by the Internet Archive. It’s quite a large chunk of data – I’m grabbing just the Stack Overflow stuff now and intend to use it for some presentations that are in the works.

Now versus UtcNow

This is a minor topic, but it was such a drastic difference when I found out about it, that it’s definitely worth sharing. Consider the following code:

 var begin = System.DateTime.UtcNow;
 for (var i = 0; i < 10000000; i++)
 var foo = System.DateTime.Now;

The result on my machine was 9150 milliseconds. Now try this:

 var begin = System.DateTime.UtcNow;
 for (var i = 0; i < 10000000; i++)
 var foo = System.DateTime.UtcNow;

The result was 152 milliseconds. Really. Not kidding. Yes, there are better ways of measuring, but that one worked well enough in this case. Apparently, the framework uses UTC natively, and then converts to local times when it matters. And that conversion comes with a huge cost!

Now you know. And knowing is half the battle.

/* Why didn’t I think of this years ago? /*

So, I attended the PASS Summit last week in Seattle. If you’ve never been, I highly recommend it. Especially if it’s in Seattle. Yes, the character of the town is basically dark, rainy, and uphill both ways. But the coffee is good, the seafood is better (especially for a midwesterner like me), and the place is just simply one of the nicer downtowns I’ve seen.

But the PASS Summit itself is also worth it, Seattle or not. You pick up a lot of great stuff, much of it at a very deep level. Adam Machanic’s pre-con on Tuesday, for instance, was such a deep dive into parallelism that my eyes were glazed over by the end of it.

And then there are the little things. The stuff that make you want to kick yourself for not thinking of it before.

I write a lot of dynamic SQL. I have tons of stored procedures that include this little gem:


And then hundreds of lines of conditionals and string concatenation and parameter substitution, followed by sp_executesql.

But what had never occurred to me was this:

DECLARE @stmt NVARCHAR(MAX) = N'/* dbo.NameOfCallingStoredProcedure */ ';


And now when it’s six months later, and I get a call from a DBA who’s trying to troubleshoot a query plan that’s gone sideways, and neither of us know or remember what’s actually generating that query.

It’s the little things, sometimes, that make the big difference.

By the way, this came from Jeremiah Peschka’s AD-302 session, Dynamic SQL: Build Fast, Flexible Queries.