Understanding the Syntax for COALESCE - sql

I am trying to understand the below syntax. Can I get some help with this.
DECLARE #StringList VARCHAR(2500);
SELECT COALESCE(#StringList + ',','') + CAST(apID as VARCHAR) AS ApIdList FROM testTable

As a result you will get all apID from testTable in VARCHAR
COALESCE
checks if first parameter is NULL then the second parameter will returned. In this line #StringList is always equals NULL
COALESCE(#StringList + ',','')
So, NULL + ',' = NULL and you will get empty string ('')
Then empty string + CAST(apID as VARCHAR) and you will get apID as VARCHAR

Coalesce returns the first non-null element provided in the list supplied. See - https://msdn.microsoft.com/en-us/library/ms190349.aspx.
In your case, if #StringList is not null, then it's contents will be prepended with a comma to appID for each row in testTable.

Your code is returning ApID as a string for all rows in the table. Why? Because #StringList is NULL so the first expression evaluates to '' and the second to a string representation of the ApId in some row in the table.
I caution you about the conversion to VARCHAR with no length. Don't do this! The default length varies by context, and you can introduce very hard-to-debug errors without a length.
A related expression is more common, I think:
SELECT #StringList = COALESCE(#StringList + ',', '') + CAST(apID as VARCHAR(8000)) AS ApIdList
FROM testTable;
This does string concatenation, so all the values of apID are concatenated together in a comma-delimited string.
What this is doing is looping on the result set to assign the variable. This type of assignment of a variable across multiple rows is discouraged. I don't think that SQL Server guarantees that it actually works (i.e. is a documented feature), but it appears to work in practice across all versions.

Related

Concate Primary Keys in SQL

I want to concate Primary Keys of multiple tables in SQL directly. I used below query to concate three primary keys with a hyphen between them but the SQL skipped the hyphen and sum up the primary keys and result in a single value.
SELECT CID + '-' + RID + '-'+ CGID As [IdCombination] ...
where CID , RID and CGID are the Primary Keys of three SQL Tables.
How it skipped the string part in query ?
Any help would be highly appreciated.
Updated
For Example : The Values of CID , RID and CGID are 3 , 4, 3 respectively. It should be 3-4-3 but the result is 10.
What is happening? Remember that + means both addition and string concatenation. It so happens that - can be interpreted as a number (like -0), so SQL Server prefers to interpret the + as addition.
Normally, when you do this type of operation, the separation character cannot be interpreted as a number, and you just get an error. I am amused that you don't get an error in this case.
One method is to explicitly cast the values as strings:
SELECT CAST(CID as VARCHAR(255)) + '-' + CAST(RID + as VARCHAR(255)) '-'+ CAST(CGID as VARCHAR(255)) As [IdCombination]
In SQL Server 2012+, you can do this more simply using CONCAT():
SELECT CONCAT(CID, '-', RID, '-', 'CGID) As [IdCombination]
CONCAT() knows that everything should be a string.
Try this
SELECT 5 + '-' + 8
The output is 13. I must admit, that I did not expect this...
And now try this
SELECT CAST('-' AS INT)
The result is 0. As your select starts with an INT, SQL Server tries to do a summa of int values. As the single hyphen is casteable to int implicitly, this returns the summa of your values...
The solution, as pointed out by others is either a cast of your column values to a string type or the usage of CONCAT
I would need to see output, but I am assuming, some ID is stored as int and it is being counted, so you should use
SELECT Cast(CID as Varchar(50)) + '-' + Cast(RID as Varchar(50)) + '-'+ Cast(CGID as Varchar(50)) As [IdCombination]

ISNULL returns 0 for a hardcoded column with NULL value

Why this statement return 0 instead of empty string? I am actually using a view which select statement containg hardcoded column return NULL. While I am trying to check if its a NULL, it actually returns 0.
SELECT ISNULL((SELECT NULL as Col), '')
Any suggestion or help would be appreciated...
snapshot
NULL does not have a clear type, but in a plain SELECT NULL, it gets returned as type int. When you have an expression involving an int and a char(N), int wins, the char(N) value gets converted to int, not the other way around. To make things more confusing, '' happens to be convertible to int without any problem, and the result of the conversion is 0.
SELECT ISNULL((SELECT CAST(NULL AS char(1)) AS col), '') should return an empty string.

SQL Server: ISNULL on uniqueidentifier

I am trying to compare a column col1 and a variable #myvar in a WHERE clause. Both usually contain GUIDs, but may also have NULL values.
I thought I could get around the fact that NULL=NULL evaluates to FALSE by using WHERE ISNULL(col1, '')=ISNULL(#myvar, ''). That would compare two empty strings instead, and evaluate to TRUE.
This will, however, produce the following error message:
Msg 8169, Level 16, State 2, Line 3 Conversion failed when converting
from a character string to uniqueidentifier.
I tried
DECLARE #myvar uniqueidentifier = NULL
SELECT ISNULL(#myvar,'') as col1
Same error message.
Two questions:
First, I am trying to convert a uniqueidentifier variable - even though it has a NULL value - to an (empty!) string, not the other way around, as the error message suggests. What gives?
Second, is there a better way to word that WHERE clause I need, to allow for comparing uniqueidentifiers that might be NULL?
I think below expression can be used to check if the GUID column is empty
CAST(0x0 AS UNIQUEIDENTIFIER)
some thing like
...WHERE GuidId <> CAST(0x0 AS UNIQUEIDENTIFIER)
Since the first argument you are passing isnull is not a literal null, it will determine the return type of that call, a uniqueidentifier in your case. The second argument, '', cannot be cast to this type, hence the error you're getting.
One way around this is just to explicitly check for nulls:
WHERE (#myvar IS NULL AND col1 IS NULL) OR (col1 = #myvar)
The reason ISNULL isn't working for you is that the replacement value (the value to be used if the check expression really is null) must be implicitly convertible to the type of the check expression.
Your WHERE clause can use a col IS NULL AND #var IS NULL to check that state.
As others have pointed out, exclude the NULL values from the results and THEN do the comparison. You can use COALESCE to exclude NULL values from comparisons.
Try the following code:
WHERE ISNULL([Guid], NEWID()) = #myvar
Here is another way to overcome this issue:
DECLARE #myvar uniqueidentifier = NEWID()
SELECT * FROM TABLE
Where ISNULL(col1,#myvar) = ISNULL(Col2,#myvar)
This will resolve your error. Conversion failed when converting from a character string to uniqueidentifier.
I needed something similar on a where clause to compare 2 fields.
Declaring a uniqueidentifier variable is causing performance issues.
So I've used something like this.
WHERE COALESCE(Table1.Field1, CAST('00000000-0000-0000-0000-000000000000' AS UNIQUEIDENTIFIER))=COALESCE(Table2.Field2, CAST('00000000-0000-0000-0000-000000000000' AS UNIQUEIDENTIFIER))

what is the use of coalesce in sql?

I'm new to sql server. I googled the use of coalesce and find out it is another words a replace of ISNULL.
I came across a piece of code posted in the forum about the different uses of coalesce.
use adventureworks
DECLARE #DepartmentName VARCHAR(1000)
SELECT #DepartmentName = COALESCE(#DepartmentName,'') + Name + ';'
FROM HumanResources.Department
WHERE (GroupName = 'Executive General and Administration')
SELECT #DepartmentName AS DepartmentNames
which return the result in a single line.
So why sql does not support string concatenation by default as in .NET like below?
DECLARE #DepartmentName VARCHAR(1000)
**SELECT #DepartmentName = #DepartmentName + Name + ';'**
FROM HumanResources.Department
WHERE (GroupName = 'Executive General and Administration')
SELECT #DepartmentName AS DepartmentNames
what is the use of coalesce in the below line
SELECT #DepartmentName = COALESCE(#DepartmentName,'') + Name + ';'
and why
**SELECT #DepartmentName = #DepartmentName + Name + ';'**
FROM HumanResources.Department
WHERE (GroupName = 'Executive General and Administration')
is not working?
coalesce simply returns the value of the 1st non-NULL argument in its parameter list.
e.g.
1.select coalesce(NULL,0,2,1);
will return 0 as 0 doesn't mean NULL.
2.select coalesce(id,emp_id,0) from tab1;
In this case attribute-id will be returned if it is NOT NULL otherwise if emp_id is NOT NULL emp_id will be returned else 0 will be returned.
In this case for concatenation you can simply use either +-operator or concat()-function. But here coalesce is used for the case when DepartmentName=NULL because if you concatenate or do some operation with NULL the result will be NULL. Hence to use blank(i.e. '') in place of NULL coalesce() has been used.
In this case coalesce has been used as DepartmentName is NULL at the moment when it is declared. Use the syntax Moho has given in his answer to substitute the use of coalesce.
add set #DepartmentName = '' after DECLARE #DepartmentName VARCHAR(1000) and you won't need the coalesce statement.
edit: updated after comment clarified things for me
COALESCE() returns the first non-null term. This code:
COALESCE(#DepartmentName,'')
means return a blank if the department name is null.
Concatenating a null with something gives a null, so if you don't use COALESCE() you'd lose the name value too.
COALESCE returns from a list of arguments the first expression that does not evaluate to NULL.
For your example COALESCE(#DepartmentName,'') this means that if #DepartmentName is NULL it shall return '' (an empty string) instead. This is needed because concatenating strings with a null value would return NULL.
SELECT 'test' + NULL -- will return NULL
COALESCE (Transact-SQL)

Concatenate sql values to a variable

On a SQL Server 2008 I'm trying to get a comma separated list of all selected values into a variable.
SELECT field
FROM table
returns:
+-------+
| field |
+-------+
| foo |
+-------+
| bar |
+-------+
I'd like to get:
"foo, bar, "
I tried:
DECLARE #foo NVARCHAR(MAX)
SET #foo = ''
SELECT #foo = #foo + field + ','
FROM TABLE
PRINT #foo
Which returns nothing. What am I doing wrong?
You'll need to change NULLs
SELECT #foo = #foo + ISNULL(field + ',', '')
FROM TABLE
or remove them
SELECT #foo = #foo + field + ','
FROM TABLE
WHERE field IS NOT NULL
That happens if you have even a SINGLE field in the table that is NULL. In SQL Server, NULL + <any> = NULL. Either omit them
SELECT #foo = #foo + field + ','
FROM TABLE
WHERE field is not null
Or work around them
SELECT #foo = #foo + isnull(field + ',', '')
FROM TABLE
You can write the whole thing without the leading SET statement which is more common. This query below returns "foo,bar" with no trailing comma
DECLARE #foo NVARCHAR(MAX)
SELECT #foo = isnull(#foo + ',', '') + field
FROM TABLE
WHERE field is not null
PRINT #foo
Don't forget to use LTRIM and RTRIM around #foo (when data type is char/varchar) in the concatenation other it will not give expected results in SQL 2008 R2.
As per the comment Lukasz Szozda made on one of the answers here, you should not use your indicated method to aggregate string values in SQL Server, as this is not supported functionality. While this tends to work when no order clause is used (and even if no exception to this tendency has ever been documented), Microsoft does not guarantee that this will work, and there's always a chance it could stop working in the future. SQL is a declarative language; you cannot assume that behaviour that is not explicitly defined as being the correct behaviour for interpreting a given statement will continue working.
Instead, see the examples below, or see this page for a review of valid ways to achieve the same result, and their respective performance: Optimal way to concatenate/aggregate strings
Doing it in a valid way, whichever way you end up using, still has the same considerations as in the other answers here. You either need to exclude NULL values from your result set or be explicit about how you want them to be added to the resulting string.
Further, you should probably use some kind of explicit ordering so that this code is deterministic - it can cause all sorts of problems down the line if code like this can produce a different result when running on the same data, which may happen without an explicit ordering specified.
--Null values treated as empty strings
SET #Foo =
STUFF /*Stuff is used to remove the seperator from the start of the string*/
( (SELECT N','/*separator*/ + ISNULL(RTRIM(t.Field), '' /*Use an emptry string in the place of NULL values*/) /*Thing to List*/
FROM TABLE t
ORDER BY t.SomeUniqueColumn ASC /*Make the query deterministic*/
FOR XML PATH, TYPE).value(N'.[1]',N'varchar(max)')
,1
,1 /*Length of separator*/
,N'');
--Null values excluded from result
SET #Foo =
STUFF /*Stuff is used to remove the seperator from the start of the string*/
( (SELECT N','/*separator*/ + RTRIM(t.Field) /*Thing to List*/
FROM TABLE t
WHERE t.Field IS NOT NULL
ORDER BY t.SomeUniqueColumn ASC /*Make the query deterministic*/
FOR XML PATH, TYPE).value(N'.[1]',N'varchar(max)')
,1
,1 /*Length of separator*/
,N'');