Invisible characters in nvarchar fields? - sql-server-2008-r2

Why does charindex returns a value other than 0 in SQL Server 2008 R2 in the following script?
--drop table #tmp
--go
create table #tmp (field1 nvarchar(50))
go
insert into #tmp values (N'Value 1')
go
insert into #tmp values (N'Value 2')
go
insert into #tmp values (N'Value 3')
go
DECLARE #i INT;
DECLARE #c NCHAR;
declare #nvc nvarchar(50) = N'Test Value';
SET #i = 128;
WHILE #i < 256
BEGIN
SET #i = #i + 1;
SET #c = nchar(#i);
if exists (select 1 from #tmp t where charindex (#c, t.field1) > 0)
print #c;
END
The output produced on my db instance (collation used: SQL_Latin1_General_CP1_CI_AS):
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
²
³
¹

The character '2' and 'superscript 2' both have the same unicode numeric value of 2 so I would assume that SQL server is treating them as equivalent values based on this component of the unicode character rather than just the way it looks.

Related

How to check for the correct datetime format in TSQL function?

The function below expects following string
22.03.2016
28.02.2017 00:00:00
NULL
Sometimes the column contains garbage like '22.003.2016'.
How to manage this situation in T-SQL with SQL Server 2008 R2?
ALTER FUNCTION [dbo].[GetItemDocDetailDataInput]
(#ItemDocDetailID uniqueidentifier)
RETURNS datetime2(7)
AS
BEGIN
DECLARE #str nvarchar(20);
DECLARE #dataInput datetime2(7);
SELECT #str = ISNULL([DataInput], null)
FROM
(SELECT
ISNULL([Value], '') AS [Value],
CASE
WHEN [Value] IS NULL THEN NULL
ELSE SUBSTRING([Value], 0, 11)
END AS DataInput
FROM
ItemDocDetailParams
WHERE
ItemDocDetailID = #ItemDocDetailID
AND ItemParamTypeID = 12) D
SELECT #dataInput = CONVERT(datetime2(7), #str, 103)
RETURN #dataInput;
END
I assume the best approach is create another tsql function where I could check for dot positions in strings like
22.03.2016
28.02.2017 00:00:00
So I will use combination of following statements
SELECT CHARINDEX('.', '13.05.2019') AS FirstDot;
3 -- ОК
SELECT CHARINDEX('.', '13.05.2019', 4) AS SecondDot;
6 -- ОК
SELECT CHARINDEX('.', '13.005.2019', 4) AS MatchPosition;
7 --- NOT OK

SQL Server 2012: check if is uniqueidentifier and convert to string

I am trying to test if a variable is a uniqueidentifier and if it is then convert it to string but it fails:
Declare #Item VARCHAR(50)
Declare #OutString varchar(max) ;
--#Outstring is populated from various tables each cell separated by ','
--Getting the #Item from the #Outstring and convert it if its uid
DECLARE #Pos INT
DECLARE #Loop BIT
SELECT #Loop = CASE WHEN LEN(#OutString) > 0 THEN 1 ELSE 0 END
WHILE (SELECT #Loop) = 1
BEGIN
SELECT #Pos = CHARINDEX(',', #OutString, 1)
IF #Pos > 0
BEGIN
SELECT #Item = SUBSTRING(#OutString, 1, #Pos - 1)
SELECT #OutString = SUBSTRING(#OutString, #Pos + 1, LEN(#OutString) - #Pos)
IF (TRY_CONVERT(UNIQUEIDENTIFIER, #Item) IS NOT NULL)
BEGIN
CONVERT(NVARCHAR(50), #Item) AS #Item --ERROR LINE incorrect syntax
END
END
END
it is either
select #Item = convert(nvarchar(50), #Item)
or
select #Item = cast(#Item as nvarchar(50))
The syntax error is because you have said the action but not what SQL should do with it.
Do you want the string to be returned?
SELECT CONVERT(NVARCHAR(50), #Item) AS Item
Appended to #Item? (but this variable is inside your loop?)
SET #Item += CONVERT(NVARCHAR(50), #Item)
Not sure what you want to do once you have converted the string. Maybe you need another variable to add the string on (like above except not SET #Item)
Use
SELECT convert(nvarchar(50), #Item ) as Item

Selecting Persons Name beginning on the right

I know how to do a left, right and substring in T-SQL, but I'm having difficulty extracting just the name of the person below since the length of the name are not the same. Any ideas or syntax that I can use to extract just the name? Thanks
Data Value:
581;#Jackson, Daniel H; 501;#Sims, Katy L; 606;#Lawrence, Jennifer O
You can get the length of the name dynamically using PATINDEX, but it assumes that the Names are ALWAYS formatted the same way.
Here is an example of a TSQL Select that will give select the first names from the data you supplied:
CREATE TABLE #Temp (
ID INT NOT NULL,
FullName VARCHAR(100)
)
INSERT #Temp VALUES (581, 'Jackson, Daniel H')
INSERT #Temp VALUES (606, 'Lawrence, Jennifer O')
SELECT ID, FullName , SUBSTRING(FullName, PATINDEX('%, % _', FullName) + 2,
PATINDEX('% _', FullName) - PATINDEX('%, %', FullName) - 2) FirstName
FROM #Temp
DROP TABLE #Temp
I use a function to split up CSV-Strings:
create function Do_Split
(#InputString NVARCHAR(4000)
,#Delimiter NVARCHAR(50) = ';')
RETURNS #Items TABLE (Item NVARCHAR(4000)) AS
BEGIN --Function
IF (#Delimiter = ' ')
BEGIN
SET #Delimiter = ';'
SET #InputString = REPLACE(#InputString, ' ', #Delimiter)
END;
IF (#Delimiter IS NULL OR #Delimiter = '') SET #Delimiter = ';';
DECLARE #Item NVARCHAR(4000)
DECLARE #ItemList NVARCHAR(4000)
DECLARE #DelimIndex INT
SET #ItemList = #InputString;
SET #DelimIndex = CHARINDEX(#Delimiter, #ItemList, 0);
WHILE (#DelimIndex != 0)
BEGIN
SET #Item = SUBSTRING(#ItemList, 0, #DelimIndex);
INSERT INTO #Items VALUES (#Item);
-- Set #ItemList = #ItemList minus one less item
SET #ItemList = SUBSTRING(#ItemList, #DelimIndex+1, LEN(#ItemList)-#DelimIndex);
SET #DelimIndex = CHARINDEX(#Delimiter, #ItemList, 0);
END; -- End WHILE
IF #Item IS NOT NULL -- At least one delimiter was encountered in #InputString
BEGIN
SET #Item = #ItemList;
INSERT INTO #Items VALUES (#Item);
END;
ELSE -- No delimiters were encountered in #InputString, so just return #InputString
BEGIN
INSERT INTO #Items VALUES (#InputString);
END;
RETURN
END -- End Function
go
Usage:
SELECT * FROM Do_Split('581;#Jackson, Daniel H; 501;#Sims, Katy L; 606;#Lawrence, Jennifer O',';');
Result:
581
#Jackson, Daniel H
501
#Sims, Katy L
606
#Lawrence, Jennifer O
It isn't clear how your data looks like, because your example concatenates multiple values in one row.
Case 1
Assume that there're 2 columns id and full_name in your_table. The semicolon ; was added by you intentionally to distinguish columns. In this case, you can obtain the value of full name using the function RIGHT. The length would be the length of full_name minus 1 which excludes the #.
--
-- id full_name
-- -- ---------
-- 581 #Jackson, Daniel H
-- 501 #Sims, Katy L
-- 606 #Lawrence, Jennifer O
--
SELECT RIGHT(full_name, LEN(full_name) - 1) AS full_name
FROM your_table;
Case 2
If the above solution is not suitable, let's discuss another case. Assume that there's 1 column content in your_table and 3 rows. The semicolon ; was inside the value of content and need to be treated explicitly. In this case, you can obtain the value of full name using the function SUBSTRING. The full name will begin after just after the index of char # and this index can be obtained using CHARINDEX (Transact-SQL):
--
-- content
-- -------
-- 581;#Jackson, Daniel H
-- 501;#Sims, Katy L
-- 606;#Lawrence, Jennifer O
--
SELECT SUBSTRING(content, CHARINDEX('#', content) + 1, 1000) AS full_name
FROM your_table;

if Statement, not working as expected when checking length of column in TSQL

I want to only update the size of a column, if it's currently smaller than my desired size. This statement below makes sense to me, but when I run it...it's coming out that an alter would occur. Why is #len < 255? I can verify that the column in question is actually 255.
I can also verify that 'select #len - 1' comes out to 254.
DECLARE #len int
SET #len = (select Cast(character_maximum_length As int)
from information_schema.columns
where table_name = 'myTable'
And column_name = 'myCol')
IF (#len < 255)
BEGIN
Print 'No work.'
END
ELSE
BEGIN
Print 'Alter table myTable Alter Column myCol varchar (255)'
END
You have swapped the condition. You would change the field if the length is smaller than 255:
IF (#len < 255)
BEGIN
Print 'Alter table myTable Alter Column myCol varchar (255)'
END
ELSE
BEGIN
Print 'No work.'
END

Add comma every nth character in value

my problem is pretty simple. I get a value from a sql select which looks like this:
ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG
and I need it like this:
AR,AM,AU,BE,BA,BR,BG,CN,DK,DE,EE,FO,FI,FR,GE,GR,IE,IS,IT,JP,YU,CA,KZ,KG
The length is different in each dataset.
I tried it with format(), stuff() and so on but nothing brought me the result I need.
Thanks in advance
With a little help of a numbers table and for xml path.
-- Sample table
declare #T table
(
Value nvarchar(100)
)
-- Sample data
insert into #T values
('ARAMAU'),
('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG')
declare #Len int
set #Len = 2;
select stuff(T2.X.value('.', 'nvarchar(max)'), 1, 1, '')
from #T as T1
cross apply (select ','+substring(T1.Value, 1+Number*#Len, #Len)
from Numbers
where Number >= 0 and
Number < len(T1.Value) / #Len
order by Number
for xml path(''), type) as T2(X)
Try on SE-Data
Time to update your resume.
create function DontDoThis (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result += #token + case when DATALENGTH(#string) = 0 then '' else ',' end
end
return #result
end
Call:
declare #test varchar(max) = 'ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG'
select dbo.DontDoThis(#test, 2)
gbn's comment is exactly right, if not very diplomatic :) TSQL is a poor language for string manipulation, but if you write a CLR function to do this then you will have the best of both worlds: .NET string functions called from pure TSQL.
I believe this is what QQping is looking for.
-- select .dbo.DelineateEachNth('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',2,',')
create function DelineateEachNth
(
#str varchar(max), -- Incoming String to parse
#length int, -- Length of desired segment
#delimiter varchar(100) -- Segment delimiter (comma, tab, line-feed, etc)
)
returns varchar(max)
AS
begin
declare #resultString varchar(max) = ''
-- only set delimiter(s) when lenght of string is longer than desired segment
if LEN(#str) > #length
begin
-- continue as long as there is a remaining string to parse
while len(#str) > 0
begin
-- as long as know we still need to create a segment...
if LEN(#str) > #length
begin
-- build result string from leftmost segment length
set #resultString = #resultString + left(#str, #length) + #delimiter
-- continually shorten result string by current segment
set #str = right(#str, len(#str) - #length)
end
-- as soon as the remaining string is segment length or less,
-- just use the remainder and empty the string to close the loop
else
begin
set #resultString = #resultString + #str
set #str = ''
end
end
end
-- if string is less than segment length, just pass it through
else
begin
set #resultString = #str
end
return #resultString
end
With a little help from Regex
select Wow=
(select case when MatchIndex %2 = 0 and MatchIndex!=0 then ',' + match else match end
from dbo.RegExMatches('[^\n]','ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',1)
for xml path(''))