CHARINDEX issue with NULL CHAR variable - sql

DECLARE #MyChar CHAR = NULL
SELECT CHARINDEX(' ', ISNULL(NULL, '')),
CHARINDEX(' ', ISNULL(#MyChar, '')),
CHARINDEX(' ', ISNULL(CONVERT(VARCHAR, #MyChar), ''))
The above query returns the values 0, 1 and 0, in that order.
This result should be 0, 0 and 0. Is this an issue with MS SQL or there is some functionality here which I haven't understood?

I belive this will answer the question:
DECLARE #MyChar CHAR = NULL
SELECT CHARINDEX(' ', ISNULL(NULL, '')) a,
CHARINDEX(' ', ISNULL(#MyChar, '')) b,
CHARINDEX(' ', ISNULL(CONVERT(VARCHAR, #MyChar), '')) c
Results:
a b c
----------- ----------- -----------
0 1 0
Testing the values:
SELECT '|' + #MyChar + '|' a,
'|' + ISNULL(#MyChar, '') + '|' b,
'|' + ISNULL(CONVERT(VARCHAR, #MyChar), '') + '|' c
Results:
a b c
---- ---- --------------------------------
NULL | | ||
The ISNULL method returns the data type of the first argument it receives. since char has a minimum length of 1, and will pad the value with trailing spaces if needed, the result of ISNULL(#MyChar, '') is a string with a single space, hence the 1 you get in your result.

Let's try to understand the second query in two parts.
First part: SELECT ISNULL(#MyChar, '')
As per MSDN regarding ISNULL function:
Data type determination of the resulting expression is determined based on the data type of the first parameter.
So your first parameter #MyChar which is of Char and its value is NULL and when you use it in ISNULL function, second parameter which is '' (blank) will implicitly converted to CHAR like this -
SELECT CAST('' AS CHAR)
When you execute this query it'll give you whitespace.
Now when you execute your actual query with CharIndex
SELECT CHARINDEX(' ', ISNULL(#MyChar, '')
You'll get 1

Since you have defined the variable as CHAR and given the value as NULL hence it would occupy some space(2 bytes). So if the variable is a fixed width column and if you are trying to store NULL value in it then it will occupy the same amount of space as any other. From here:
There is a misconception that if we have the NULL values in a table it
doesn't occupy storage space. The fact is, a NULL value occupies space
– 2 bytes
If you change the datatype to varchar(1) and then provide the value as NULL you will find that you are getting the result as 0,0,0. So in case of variable width provided to the variable the NULL takes no space.
A good read article: How does SQL Server really store NULL-s

Related

Removing spaces in complex names

I am trying to insert complex names like Juan Carlos but I want to remove all the spaces except the one between Juan and Carlos. Lets use # as space to see spaces better.
When inserting I have tried RTRIM(LTRIM(#Name)) however It seems not to work, I tried to insert ###Jua#Car### but when I select the field with DATALENGTH([Name]) I get the lenght of 14.
As I see that string I can count 13 characters, not 14.
1. What is the character I cannot count?
2. How can I end up getting Juan#Carlos removing all the spaces if LTRIM and RTRIM does not work?
Update with more info:
The column datatype is nvarchar(100)
I just tried REPLACE([Name], ' ','') and the lenght i get is 12
You can trim non-alphanumeric characters using a somewhat complicated method:
select t2.name2
from t outer apply
(select (case when name like '%[a-zA-Z0-9]%'
then stuff(t.name, 1, patindex(t.name, '%[a-zA-Z0-9]%'), '')
else ''
end) as name1
) t1 outer apply
(select (case when name1 like '%[a-zA-Z0-9]%'
then left(t1.name1,
len(t1.name1) - patindex(reverse(t.name), '%[a-zA-Z0-9.]%')
)
else ''
end) as name2
) t2
Try to use in following, select separately FirstName, LastName and concat them with space:
DECLARE #FullName VARCHAR(MAX) = ' Juan Carlos '
SELECT LEN(SUBSTRING(LTRIM(RTRIM(#FullName)), 1, CHARINDEX(' ', LTRIM(RTRIM(#FullName))) - 1) + ' ' +
REVERSE(SUBSTRING(REVERSE(LTRIM(RTRIM(#FullName))), 1,
CHARINDEX(' ', REVERSE(LTRIM(RTRIM(#FullName)))) - 1) )) AS [Len]
It returning len = 11

SQl string split (parse) on space where some field values have no space

I've been trying to split a string value in my query to return the first part of a two part postcode in a new column. Some values have only the first part, and some have both. After a bit of searching I found this code:
SELECT
TblPLSSU.PLSSUPostcode
,SUBSTRING(TblPLSSU.PLSSUPostcode, 1, CHARINDEX(' ', TblPLSSU.PLSSUPostcode)) AS PCode
FROM
TblPostcodes
This happily splits the values that have two parts to the postcode but it seems to be ignoring the single part post codes.
For example, values for TblPLSSU.PLSSUPostcode might be:
EH1 1AB
EH2
I want to return the values
EH1
EH2
But with the code above I am only getting EH1
Thanks
Eils
Use case to pick up post codes as-is when they don't have a space separated value.
SELECT
PLSSUPostcode
,case when CHARINDEX(' ', PLSSUPostcode) > 0
then SUBSTRING(PLSSUPostcode, 1, CHARINDEX(' ', PLSSUPostcode))
else PLSSUPostcode end AS PCode
FROM
TblPostcodes
Use case as well:
SELECT TblPLSSU.PLSSUPostcode,
(CASE WHEN TblPLSSU.PLSSUPostcode LIKE '% %'
THEN SUBSTRING(TblPLSSU.PLSSUPostcode, 1, CHARINDEX(' ', TblPLSSU.PLSSUPostcode))
END) AS PCode
FROM . . .
A little trick with STUFF function. This works because of CHARINDEX returns index of the first occurrence. So you are just adding space to strings and replacing all symbols from first occurrence till the end with empty string:
DECLARE #t TABLE(v VARCHAR(20))
INSERT INTO #t VALUES('EH1 1AB'), ('EH2')
SELECT STUFF(v + ' ', CHARINDEX(' ', v + ' '), LEN(v), '')
FROM #t
Another version:
SELECT SUBSTRING(v + ' ', 1, CHARINDEX(' ', v + ' ') - 1)
FROM #t
Output:
EH1
EH2

Convert varchar to 3 (sometimes 4) chars in T-SQL

I select data from a database. The values are (field name is ADR_KOMP_VL) :
4 , 61A, 100, 12, 58, 123C, 6 A, 5
I need to convert these values to 3 digits (except when there is a letter then it is 4)
So the converted values should be:
004, 061A, 100, 012, 058, 123C, 006A, 005
The rules are:
Always 3 digits
No spaces
If the original value is less than three digits, put 0's in front of it.(The length is 3)
If the original value contains a letter, put 0's in front of it (but the length is 4)
For the "no space" part I have this:
select REPLACE(ADR_KOMP_VL, ' ','')
The solution I have so far is:
SELECT RIGHT('000' + CONVERT(VARCHAR(4),REPLACE(ADR_KOMP_VL, ' ','')), 3)
But this only gives me the right length, when there is no letter in the value. My problem is how to handle the values with a letter in them??
This only check if the last character is letter. Additional logic will be required if that's not the case
SELECT REPLICATE('0', CASE WHEN ISNUMERIC(RIGHT(ADR_KOMP_VL, 1)) = 0 THEN 4
ELSE 3
END - LEN(REPLACE(ADR_KOMP_VL, ' ', '')))
+ REPLACE(ADR_KOMP_VL, ' ', '')
FROM TX
EDIT - actually this might work better, checks for whole ADR_KOMP_VL if it's numeric:
SELECT REPLICATE('0', CASE WHEN ISNUMERIC(REPLACE(ADR_KOMP_VL, ' ', '')) = 0 THEN 4
ELSE 3
END - LEN(REPLACE(ADR_KOMP_VL, ' ', '')))
+ REPLACE(ADR_KOMP_VL, ' ', '')
FROM TX
SQLFiddle DEMO
You can use a case statement:
SELECT (case when ADR_KOMP_VL like '%[A-Z]%'
then RIGHT('0000' + CONVERT(VARCHAR(4),REPLACE(ADR_KOMP_VL, ' ','')), 4)
else RIGHT('000' + CONVERT(VARCHAR(4),REPLACE(ADR_KOMP_VL, ' ','')), 3)
end)

Using ISNULL when adding NULL to varchar

Whilst experimenting with MSSQL I came across some behaviour I cannot explain.
I was looking at what happens to a NULL value when it is added to a varchar, and when I do the following query:
SELECT
ISNULL(NULL + ' ', 'test')
I get the result 'te'. Similarly if I change the word test for any other word I only get the first two letters. If I increase the number of spaces in the + ' ' I get extra letters in my result (so NULL + '[two spaces]' gives me 'tes'). Any ideas what is going on?
If I declare a variable and set it to NULL e.g.
DECLARE #testnull AS varchar(32)
SET #testnull = NULL
SELECT
ISNULL(#testnull + ' ', 'test')
then I get the result 'test' (as I would expect).
Use COALESCE. ISNULL takes the first NON-NULL data type, and in this case because it hasn't been declared, you get a VARCHAR(1) for the NULL and then it becomes a VARCHAR(2) when you add the space (this is still NULL when evaluated but SQL Server makes an assumption before that step). In some cases the default for varchar without length is 1, and in others it is 30. I'll let you guess which one is being used here.
SELECT
ISNULL(NULL + ' ', 'test'), COALESCE(NULL + ' ', 'test');
Results:
---- ----
te test
You can see this explicitly by:
SELECT
x = ISNULL(NULL + ' ', 'test')
INTO #a1;
SELECT
x = COALESCE(NULL + ' ', 'test')
INTO #a2;
SELECT LEFT(t.name, 3), TYPE_NAME(c.user_type_id), max_length
FROM tempdb.sys.columns AS c
INNER JOIN tempdb.sys.tables AS t
ON c.[object_id] = t.[object_id]
WHERE t.name LIKE '#a[1-2]%';
Results:
--- ------- ----
#a1 varchar 2
#a2 varchar 4
In almost all cases, I prefer COALESCE over ISNULL. I explain why (and where the exceptions exist) in this tip:
Deciding between COALESCE and ISNULL in SQL Server

What is happening in this T-SQL code? (Concatenting the results of a SELECT statement)

I'm just starting to learn T-SQL and could use some help in understanding what's going on in a particular block of code. I modified some code in an answer I received in a previous question, and here is the code in question:
DECLARE #column_list AS varchar(max)
SELECT #column_list = COALESCE(#column_list, ',') +
'SUM(Case When Sku2=' + CONVERT(varchar, Sku2) +
' Then Quantity Else 0 End) As [' +
CONVERT(varchar, Sku2) + ' - ' +
Convert(varchar,Description) +'],'
FROM OrderDetailDeliveryReview
Inner Join InvMast on SKU2 = SKU and LocationTypeID=4
GROUP BY Sku2 , Description
ORDER BY Sku2
Set #column_list = Left(#column_list,Len(#column_list)-1)
Select #column_list
----------------------------------------
1 row is returned:
,SUM(Case When Sku2=157 Then Quantity Else 0 End) As [157 -..., SUM(Case ...
The T-SQL code does exactly what I want, which is to make a single result based on the results of a query, which will then be used in another query.
However, I can't figure out how the SELECT #column_list =... statement is putting multiple values into a single string of characters by being inside a SELECT statement. Without the assignment to #column_list, the SELECT statement would simply return multiple rows. How is it that by having the variable within the SELECT statement that the results get "flattened" down into one value? How should I read this T-SQL to properly understand what's going on?
In SQL Server:
SELECT #var = #var + col
FROM TABLE
actually concatenates the values. It's a quirks mode (and I am unable at this time to find a reference to the documentation of feature - which has been used for years in the SQL Server community). If #var is NULL at the start (i.e. an uninitialized value), then you need a COALESCE or ISNULL (and you'll often use a separator):
SELECT #var = ISNULL(#var, '') + col + '|'
FROM TABLE
or this to make a comma-separated list, and then remove only the leading comma:
SELECT #var = ISNULL(#var, '') + ',' + col
FROM TABLE
SET #var = STUFF(#var, 1, 1, '')
or (courtesy of KM, relying on NULL + ',' yielding NULL to eliminate the need for STUFF for the first item in the list):
SELECT #var = ISNULL(#var + ',', '') + col
FROM TABLE
or this to make a list with a leading, separated and trailing comma:
SELECT #var = ISNULL(#var, ',') + col + ','
FROM TABLE
You will want to look into the COALESCE function. A good article describing what is happening can be seen here.