Skip to content
Advertisement

SQL Server : ISNULL(compound NULL condition, ‘a string’) returns only the 1st character, under certain circumstance(s)

I’m a self-taught, vaguely competent SQL user. For a view that I’m writing, I’m trying to develop a ‘conditional LEFT‘ string-splitting command (presumably later to be joined by a ‘conditional RIGHT‘ – whereby:

  • If a string (let’s call it ‘haystack’) contains a particular pattern (let’s call it ‘needle’), it will be pruned to the left of that pattern
  • Otherwise, the entire string will be passed unaltered.

So, if our pattern is ‘ – ‘,

  • ‘A long string – containing the pattern’ will output as ‘A long string’
  • ‘A string without the pattern’ will be returned as-is.

Rather than using the crudest ways to do this, I’m trying to come up with a way that avoids having to repeat any clause (such as if 0 < CHARINDEX, then take CHARINDEX – 1, etc.) and instead leverages conditional NULLing.

Yet – here’s what I get for trying to be creative – I’ve hit what seems to be a really basic stumbling block. Please observe the following code and results, and let me know whether you can replicate it – and hence whether it’s a bug or I’ve missed something peculiar. I have tested this on SQL Server both 2008 R2 and 2014, both Express editions.

Why do these 2 clauses work as expected in isolation, but when I combine them, rather than getting the sum of their effects, I get only the 1st character of the ISNULL string – ‘a’?

Is there some kind of implicit CAST to varchar(1)? Deliberately casting to varchar(max) made no difference. What else could be going on here?

Am I just doing something really stupid? Because from here, I can’t figure out what I’m doing wrong, and so it really seems like a bug. I hoped testing on 2014 would prove it to be a bug in the old 2008 R2, but alas, they act identically (or, rather, don’t).

Thanks in advance for, hopefully, saving me from what would presumably be an evening of baffled existential crisis.

Advertisement

Answer

There are two parts to this problem, the first is the nature of the ISNULL operator, it will use the datatype and length of the first argument. A simple example would be:

This returns T and checking the execution plan XML we can see the implicit conversion of "This is a Test" to CHAR(1):

Your example is not quite so straightforward since you don’t have your types nicely defined like above, but if we do define the dataypes:

We get the result as expected. So something else is happening under the hood. The query plan does not delve into the inner workings of the constant evaluation, but the following demonstrates what is happening:

Basically, by using the SELECT INTO sytax with your left expression shows that a when the NULL length is passed to LEFT the resulting datatype is VARCHAR(1), however, this is not always the case. If I simply hard code NULL into the LEFT function:

Then you get the length of the string passed, but a case expression that should be optimised away to the same thing, yields a length of 1 again:

I suspect it is related to the default behaviour of VARCHAR, where the default length is 1, e.g:

But I can’t tell you why you would see different behaviour for NULL and CASE WHEN 1 = 1 THEN NULL ELSE 1 END. If you wanted to get the bottom of what is going on in the constant evaluation I think you would probably need to re-ask on the DBA site and hope that one of the real SQL Server Gurus picks it up.

In summary, LEFT(<constant>, <constant expression>) where <constant expression> yields NULL is implicitly typed as VARCHAR(1), and this implicit type is used in ISNULL evaluation.

For what it is worth, if you explicitly type the result of your LEFT function then you get the expected result:


An additional point is that when you say you don’t want to repeat any expressions (If 0 < CHARINDEX, then take CHARINDEX – 1, etc.), there are two things you should know, the first is that NULLIF(<expression>, <value>) expands to a case expression – CASE WHEN <expression> = <value> THEN NULL ELSE <expression> END, so is repeated, the second is that this doesn’t matter, SQL Server can identify that this is the same expression used twice, and will evaluate it once and refer to the same result each time it is used.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement