38
votes

In Postgres:

select 'test' || null  returns null

I was expecting it would otherwise return 'test'.

Is this desired behavior? Seems weird that string concatenation with a null value would nullify the entire string...

Referring to pg docs: http://www.postgresql.org/docs/9.1/static/functions-string.html

"Note: Before PostgreSQL 8.3, these functions would silently accept values of several non-string data types as well, due to the presence of implicit coercions from those data types to text. Those coercions have been removed because they frequently caused surprising behaviors. However, the string concatenation operator (||) still accepts non-string input, so long as at least one input is of a string type, as shown in Table 9-6. For other cases, insert an explicit coercion to text if you need to duplicate the previous behavior."

Given that, using their concat string function example:

concat(str "any" [, str "any" [, ...] ]) text Concatenate all arguments. NULL arguments are ignored. concat('abcde', 2, NULL, 22) >> abcde222

Should I just get used to this behavior with '||' concatenation or is this something that should be fixed?

4
It's not only "desired", this is how it is specified in the SQL standard. - a_horse_with_no_name
@a_horse_with_no_name - that it is SQL standard is all I really needed to know - your comment should be the answer. Seems unintuitive though. It challenged me to have a look at how other languages handle that case. C# seems to offer the intuitive - treating the null as a zero-length string. Javascript and Java concatenate "null" to your string (sheesh). - Reinsbrain
just for fun i'd like to add, aggregates seem to ignore null ;) - Reinsbrain

4 Answers

27
votes

It's not a bug and it's not "weird".

The SQL standard requires any expression that involves null yields null. This is not limited to string concatenation, it also applies to computations e.g.: 42 * null returns null.

This also applies to comparisons: 42 > null yields null. So the comparison it's neither true nor false. Although in reality this has the effect of "false", but more because it's "not true", rather then false. But negating such an expression yields null again, not "true".

Because null is so special, the only way to check if something is null is to use the operator IS NULL or IS NOT NULL. x is null yields either true or false, it never yields null, so expressions using the is null or is not null operator never return null - so this is an exception to my statement above (thanks Jonathan for pointing that out).


Another - maybe surprising - fact about null values is how they are handled by aggregate functions. While the expression 4 + 5 + null yields null, the sum() over those (column) values would yield 9, because aggregates ignore null values.

Given the following table:

    col1
--------
       1
       2
       3 
     null

sum(col1) will return 6, and avg(col1) will return 2 (sum = 6, number of elements added: 3)

35
votes

Quote from documentation

Concatenate all arguments. NULL arguments are ignored.

Example

concat('abcde', 2, NULL, 22) 

Returns

abcde222

See more in Documentation

19
votes

Yes, it is desired behavior.

Here is an example in which it is very useful. Let's say you have a persons table an in it the fields title, firstname, nameaffix and lastname. To build a full name you can simply do this.

COALESCE(title || ' ', '') || firstname || COALESCE(' ' || nameaffix, '') || ' ' || lastname

Without that strange behavior that would be quite an afford to place the spaces at the right spots depending on whether a field is null or not. Assuming a mandatory first name and last name any combination of title and name affix is covered.

5
votes

As the guy who taught me SQL said, "NULL means 'unknown' it doesn't mean 'zero', or 'empty string' it means 'unknown'".

That is the reason it is desired behavior, the result of combining something with an unknown value is an unknown.

This is also worth considering when designing new columns and what being nullable means for them.