Why is T-SQL ISNULL() truncating the string and COALESCE is not? - sql-server

Given the following:
SELECT ISNULL('XY' + NULL, 'ABCDEFGHIJ') -- Outputs ABC (Why?)
SELECT COALESCE('XY' + NULL, 'ABCDEFGHIJ') -- Outputs ABCDEFGHIJ
Why are these statements returning different results?

According to Microsoft documentation, for function:
ISNULL(check_expression, replacement_value)
replacement_value must be of a type that is implicitly convertible to the type of check_expression. Note that type for 'xy'+NULL is VARCHAR(3). Because of this your string 'ABCDEFGHIJ' is cast to VARCHAR(3) and thus trimmed.
It sounds strange why it is not VARCHAR(2), but this is the way it is - one character longer than 'xy'. You can play with this SQLFiddle and see for yourself that type for 'xy'+NULL is the same as for expression CASE WHEN 1=2 THEN 'XYZ' ELSE NULL END, which is NULL but is implicitly compatible to VARCHAR(3).
It seems that for expression 'xy'+NULL perceived length can be computed as 'xy' string length (2) plus 1 for every NULL added. For example, type of 'xy'+NULL+NULL is VARCHAR(4), type for 'xy'+NULL+NULL+NULL is VARCHAR(5) and so on - check out this SQLFiddle. This is extremely weird, but that is how MS SQL Server 2008 and 2012 work.

You can check all the difference here, its very clear
MSDN : http://msdn.microsoft.com/en-us/library/ms190349.aspx
MSDN Blog : http://blogs.msdn.com/b/sqltips/archive/2008/06/26/differences-between-isnull-and-coalesce.aspx

ISNULL() converts the replacement value to the type of the check expression. In this case, the type of the check expression is CHAR(2), so converting the replacement value truncates it (are you sure you're getting ABC and not just AB?).
From the Microsoft documentation:
replacement_value can be truncated if replacement_value is longer than check_expression.

Related

SQL CAST with ISNULL results in conversionfailure

I've got a nullable column of numbers, which is unfortunately declared as a varchar in the database, and so I want to convert the NULL to 0. Doing this accomplishes that.
select ISNULL(col, '0') from table;
So now there are no null possibilities, and then I want to convert the column to an actual int value, so I tried to wrap that.
select CAST(ISNULL(col, '0') AS INT) from table
When that runs I get a conversion error, as shown below. I don't understand why, or how to get around this:
Conversion failed when converting the varchar value 'NULL' to data type int.
Try like this: Take your ISNULL statement outside
SELECT ISNULL(TRY_CAST(col AS INT),0) AS [col]
FROM SAMPLE_TAB;
If this does not help, then please comment your SQL version.

Sql Server Computed Column Specification [duplicate]

I have this function for a computed column :
CREATE FUNCTION [dbo].[GetAllocatedStartTime](#Year INT, #Week INT)
RETURNS DATETIME
WITH schemabinding
AS BEGIN
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT([varchar](4),#Year,(0))+'-01-01'),(1))))
END
GO
I added the WITH schemabinding in the hope it would make it deterministic so I can persist it. It should be as the two inputs [Week] and [Year] will always yield the same results.
The exact error is :
Computed column 'AllocatedTimeStart' in table 'Tmp_Bookings' cannot be persisted because the column is non-deterministic.
I am using this formula in the column :
([dbo].[GetAllocatedStartTime]([Year],[Week]))
And the column defs :
[Week] [int] NOT NULL,
[Year] [int] NOT NULL,
[AllocatedTimeStart] AS ([dbo].[GetAllocatedStartTime]([Year],[Week])),
Any ideas?
EDIT:
Changed line to :
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112)),(1))))
But now I get an error saying the formula for the column is invalid. Even though the function saves fine.
EDIT 2:
I've shown exactly what I am doing (or atleast I've tried). There is nothing extra really. As it says the previous function (original one) coupled with the formula ref [dbo].AllocatedStartDate(...) to it in the column worked, but was not persisting, it said it was non deterministic. So according to the suggestion I changed the FUNCTION, replacing the conversion part with the new code, so the function now looks like :
FUNCTION [dbo].[GetSTime](#Year INT, #Week INT)
RETURNS DATETIME
WITH schemabinding
AS BEGIN
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112)),(1))))
END
Then I tried the same formula as before in the computed field (([dbo].[GetAllocatedStartTime]([Year],[Week]))) ... and it rejects the formula, says its not valid... which is strange as the formula is the same, so it must be doing some sort of check of the changed function and finding that to be invalid, which is also strange because I did a plain SELECT dbo.GetAllocatedStartTime(2012,13) and it worked...
So yes I am confused, and I've never seen SqlFiddle never mind use it. But really there is nothing more than what I have just said.
CONVERT([varchar](4),#Year,(0))+'-01-01' is being passed to a DATEDIFF call, in a position where a date is expected, forcing an implicit conversion to occur.
From the rules for deterministic functions:
CAST
Deterministic unless used with datetime, smalldatetime, or sql_variant.
CONVERT
Deterministic unless one of these conditions exists:
...
Source or target type is datetime or smalldatetime, the other source or target type is a character string, and a nondeterministic style is specified. To be deterministic, the style parameter must be a constant. Additionally, styles less than or equal to 100 are nondeterministic, except for styles 20 and 21. Styles greater than 100 are deterministic, except for styles 106, 107, 109 and 113.
Well, you're calling neither, but you're relying on an implicit conversion, which I'd expect to act like CAST. Rather than rely on this, I'd switch to using CONVERT and give a deterministic style parameter.
So, I'd do: CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112) in its place. Having done so, the function itself becomes deterministic

How to overcome limitation of ISNUMERIC column "ERROR : overflowed an int column"

I have a requirement wherein I have to check whether value entered in VARCHAR(200) column is numeric or not, and if so a relevant error message should be passed.
I am validating the same with ISNUMERIC function since my column value is varchar so user can enter more than 10 characters as well due to which I am getting this error:
overflowed an int column
Because of the other business support, I can not change the data type of the column to int.
As of now I have implemented LEN() < 10 condition before checking ISNUMERIC but seeking if any alternate and better option available.
If you work on Sql server 2012 Than its better to Use TRY_Convert() Function.it will give NULL as output rather than impose error
declare #d varchar(200)='940852774565564'
if ((select ISNUMERIC(#d))=1)
select Try_Convert(#d as bigint)
else
Convert the value to bigint rather than INT
declare #d varchar(200)='940852774565564'
if ((select ISNUMERIC(#d))=1)
select cast(#d as bigint)

attribute key was not found - MS SQL Server

I got the following message on MS SQL Server (I'm translating from German):
"Table 'VF_Fact', column ORGUNIT_CD, Value: 1185. The attribute is
ORGUNIT_CD. Row was dropped because attribute key was not found.
Attribute: ORGUNIT_CD in the dimension 'Organization' from database
'Dashboard', Cube 'Box Cube'..."
I checked the fact table 'VF_Fact' and the column ORGUNIT_CD - there I was able to found the value '1185'. The column ORGUNIT_CD is defined as follows in the view:
CAST( COALESCE( emp.ORGUNIT_CD, 99999999 ) AS char(8)) AS ORGUNIT_CD,
In addition the view retrieves the column from L_Employee_SAP TABLE, where ORGUNIT_CD is defined as follows:
[ORGUNIT_CD] [char](8) NOT NULL,
AND the value I find here is not '1185' but '00001185'.
The Fact table 'VF_Fact' is connected with the table L_ORG in which the column ORGUNIT_CD is defined as follows:
[ORGUNIT_CD] [char](8) NOT NULL,
This table hast the following value in the ORGUNIT_CD column: '00001185'.
Can anyone please explain, why am i getting this error, and how to remove it?
From this answer:
COALESCE:
Return Types
Returns the data type of expression with the highest data type precedence. If all expressions are nonnullable, the result is typed
as nonnullable.
(Emphasis added). int had a higher precedence than varchar, so
the return type of your COALESCE must be of type int. And obviously,
your varchar value cannot be so converted.
As another answer noted, ISNULL() behaves differently: rather than return the data type with the highest precedence, it returns the data type of the first value (thus, #Aleem's answer would solve your issue). A more detailed explanation can be found here under the section "Data Type of Expression."
In your specific case, I'd actually recommend that you encase the alternative string in single quotes, thus tipping SQL Server off to the fact that you intend this to be a character field. This means your expression would be one of the following:
CAST (ISNULL( emp.ORGUNIT_CD, '99999999' ) as char(8))
CAST (COALESCE( emp.ORGUNIT_CD, '99999999' ) AS char(8))
The advantage of using quotes in this situation? If you (or another developer) comes back to this down the line and tries to change it to COALESCE() or do any other type of modification, it's still going to work without breaking anything, because you told SQL Server what data type you want to use in the string itself. Depending on what else you're trying to do, you might even be able to remove the CAST() statement entirely.
COALESCE( emp.ORGUNIT_CD, '99999999' )
COALESCE function is dropping the leading zeroes. If you are checking for nulls you can do this and it will keep the zeroes.
CAST (ISNULL( emp.ORGUNIT_CD, 99999999 ) as char(8))

Unicode characters causing issues in SQL Server 2005 string comparison

This query:
select *
from op.tag
where tag = 'fussball'
Returns a result which has a tag column value of "fußball". Column "tag" is defined as nvarchar(150).
While I understand they are similar words grammatically, can anyone explain and defend this behavior? I assume it is related to the same collation settings which allow you to change case sensitivity on a column/table, but who would want this behavior? A unique constraint on the column also causes failure on inserts of one value when the other exists due to a constraint violation. How do I turn this off?
Follow-up bonus point question. Explain why this query does not return any rows:
select 1
where 'fußball' = 'fussball'
Bonus question (answer?): #ScottCher pointed out to me privately that this is due to the string literal "fussball" being treated as a varchar. This query DOES return a result:
select 1
where 'fußball' = cast('fussball' as nvarchar)
But then again, this one does not:
select 1
where cast('fußball' as varchar) = cast('fussball' as varchar)
I'm confused.
I guess the Unicode collation set for your connection/table/database specifies that ss == ß. The latter behavior would be because it's on a faulty fast path, or maybe it does a binary comparison, or maybe you're not passing in the ß in the right encoding (I agree it's stupid).
http://unicode.org/reports/tr10/#Searching mentions that U+00DF is special-cased. Here's an insightful excerpt:
Language-sensitive searching and
matching are closely related to
collation. Strings that compare as
equal at some strength level are those
that should be matched when doing
language-sensitive matching. For
example, at a primary strength, "ß"
would match against "ss" according to
the UCA, and "aa" would match "å" in a
Danish tailoring of the UCA.
The SELECT does return a row with collation Latin1_General_CI_AS (SQL2000).
It does not with collation Latin1_General_BIN.
You can assign a table column a collation by using the COLLATE < collation > keyword after N/VARCHAR.
You can also compare strings with a specific collation using the syntax
string1 = string2 COLLATE < collation >
This isn't an answer that explains behavior, but may be relevant:
In this question, I learned that using the collation of
Latin1_General_Bin
will avoid most collation quirks.
Some helper answers - not the complete one to your question, but still maybe helpful:
If you try:
SELECT 1 WHERE N'fußball' = N'fussball'
you'll get "1" - when using the "N" to signify Unicode, the two strings are considered the same - why that's the case, I don't know (yet).
To find the default collation for a server, use
SELECT SERVERPROPERTY('Collation')
To find the collation of a given column in a database, use this query:
SELECT
name 'Column Name',
OBJECT_NAME(object_id) 'Table Name',
collation_name
FROM sys.columns
WHERE object_ID = object_ID('your-table-name')
AND name = 'your-column-name'
Bonus question (answer?): #ScottCher
pointed out to me privately that this
is due to the string literal
"fussball" being treated as a varchar.
This query DOES return a result:
select 1 where 'fußball' =
cast('fussball' as nvarchar)
Here you're dealing with the SQL Server data type precedence rules, as stated in Data Type Precedence. Comparisons are done always using the higher precedence type:
When an operator combines two
expressions of different data types,
the rules for data type precedence
specify that the data type with the
lower precedence is converted to the
data type with the higher precedence.
Since nvarchar has a higher precedence than varchar, the comparison in your example will occur suing the nvarchar type, so it's really exactly the same as select 1 where N'fußball' =N'fussball' (ie. using Unicode types). I hope this also makes it clear why your last case doesn't return any row.

Resources