Fetch data after and before '_' in SQL Server - sql-server

I got the following entry in my database:
I_ABC_2000.txt
I want to trim the entry so I get: ABC
So basically, I want everything after the first _ and before the 2nd _ .
How can I do that?

There are possibly better ways to do this.. but one that I use is to use a scalar function .
Create a scalar function and then call that function over your string
CREATE FUNCTION [dbo].[fnParseString]
(
#Section SMALLINT,
#Delimiter CHAR,
#Text varchar(1000)
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #NextPos SMALLINT,
#LastPos SMALLINT,
#Found SMALLINT
IF #Section > 0
SELECT #Text = REVERSE(#Text)
SELECT #NextPos = CHARINDEX(#Delimiter, #Text, 1),
#LastPos = 0,
#Found = 1
WHILE #NextPos > 0 AND ABS(#Section) <> #Found
SELECT #LastPos = #NextPos,
#NextPos = CHARINDEX(#Delimiter, #Text, #NextPos + 1),
#Found = #Found + 1
RETURN CASE
WHEN #Found <> ABS(#Section) OR #Section = 0 THEN NULL
WHEN #Section > 0 THEN REVERSE(SUBSTRING(#Text, #LastPos + 1, CASE WHEN #NextPos = 0 THEN DATALENGTH(#Text) - #LastPos ELSE #NextPos - #LastPos - 1 END))
ELSE SUBSTRING(#Text, #LastPos + 1, CASE WHEN #NextPos = 0 THEN DATALENGTH(#Text) - #LastPos ELSE #NextPos - #LastPos - 1 END)
END
END
Then you can call the function over your text (the first argument is the position of the text you want, the second is the delimiter of your text and the thirst is the actual text)
select dbo.[fnParseString] (1,'_','abra_ka_dabra')
would return
dabra
select dbo.[fnParseString] (2,'_','abra_ka_dabra')
would return
ka
etc.

Related

Debugging rtf to text function

I have a function we use to convert RTF formatted text to plain text. It has worked pretty well in the past, and seems to work pretty well so far on the text in question.
However, somewhere in my dataset of 230,000 records, it makes a bad SUBSTRING call and aborts the entire thing (without telling me the offending record).
Is there any way I can get some feedback into what is going on?
I know that SQLServer functions do not allow PRINT statements, or INSERT statements.
And the dataset of 230,000 records is not mine, but a clients. I really don't want to have to try to go record by record and see which one is causing the error.
SQL Function below:
CREATE FUNCTION [dbo].[RTF2Text]
(
#rtf nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #Pos1 int;
DECLARE #Pos2 int;
DECLARE #hex varchar(316);
DECLARE #Stage table
(
[Char] char(1),
[Pos] int
);
INSERT #Stage
(
[Char]
, [Pos]
)
SELECT SUBSTRING(#rtf, [Number], 1)
, [Number]
FROM [master]..[spt_values]
WHERE ([Type] = 'p')
AND (SUBSTRING(#rtf, Number, 1) IN ('{', '}'));
SELECT #Pos1 = MIN([Pos])
, #Pos2 = MAX([Pos])
FROM #Stage;
DELETE
FROM #Stage
WHERE ([Pos] IN (#Pos1, #Pos2));
WHILE (1 = 1)
BEGIN
SELECT TOP 1 #Pos1 = s1.[Pos]
, #Pos2 = s2.[Pos]
FROM #Stage s1
INNER JOIN #Stage s2 ON s2.[Pos] > s1.[Pos]
WHERE (s1.[Char] = '{')
AND (s2.[Char] = '}')
ORDER BY s2.[Pos] - s1.[Pos];
IF ##ROWCOUNT = 0
BREAK
DELETE
FROM #Stage
WHERE ([Pos] IN (#Pos1, #Pos2));
UPDATE #Stage
SET [Pos] = [Pos] - #Pos2 + #Pos1 - 1
WHERE ([Pos] > #Pos2);
SET #rtf = STUFF(#rtf, #Pos1, #Pos2 - #Pos1 + 1, '');
END
SET #rtf = REPLACE(#rtf, '\pard', '^*^');
SET #rtf = REPLACE(#rtf, '\par', '^*^');
SET #rtf = REPLACE(#rtf, '\t', '^~^');
SET #rtf = STUFF(#rtf, 1, CHARINDEX(' ', #rtf), '');
IF len(#rtf) > 0
WHILE (Right(#rtf, 1) IN (' ', CHAR(13), CHAR(10), '}'))
BEGIN
SELECT #rtf = SUBSTRING(#rtf, 1, (LEN(#rtf + 'x') - 2));
IF LEN(#rtf) = 0 BREAK
END
SET #Pos1 = CHARINDEX('\''', #rtf);
WHILE #Pos1 IS NOT NULL AND #Pos1 > 0
BEGIN
IF #Pos1 IS NOT NULL AND #Pos1 > 0
BEGIN
SET #hex = '0x' + SUBSTRING(#rtf, #Pos1 + 2, 2);
SET #rtf = REPLACE(#rtf, SUBSTRING(#rtf, #Pos1, 4), CHAR(CONVERT(int, CONVERT (binary(1), #hex,1))));
SET #Pos1 = CHARINDEX('\''', #rtf);
END
END
SET #rtf = COALESCE(#rtf, '') + ' ';
SET #Pos1 = PATINDEX('%\%[0123456789][\ ]%', #rtf);
WHILE #Pos1 IS NOT NULL AND #Pos1 > 0 AND #rtf != ''
BEGIN
SET #Pos2 = CHARINDEX(' ', #rtf, #Pos1 + 1);
IF #Pos2 < #Pos1
SET #Pos2 = CHARINDEX('\', #rtf, #Pos1 + 1);
IF #Pos2 < #Pos1
BEGIN
SET #rtf = SUBSTRING(#rtf, 1, #Pos1 - 1);
SET #Pos1 = 0;
END
ELSE
BEGIN
SET #rtf = STUFF(#rtf, #Pos1, #Pos2 - #Pos1 + 1, '');
SET #Pos1 = PATINDEX('%\%[0123456789][\ ]%', #rtf);
END
END
IF RIGHT(#rtf, 1) = ' '
SET #rtf = SUBSTRING(#rtf, 1, LEN(#rtf) -1);
RETURN #rtf;
END
Not to be too rude, but have you actually tested on your function?
Have you run any unit tests to try and break your function, e.g. invalid values, boundary conditions etc.?
Have you checked documentation to see under what conditions SUBSTRING can throw an exception?
I have run these cases and am getting exceptions:
SELECT dbo.[RTF2Text]( NULL )
SELECT dbo.[RTF2Text]( '' )
SELECT dbo.[RTF2Text]( '1' )
SELECT dbo.[RTF2Text]( 'blah' )
If you know under what conditions/input values your function will fail, then it is a simple matter of checking for these in your table.
I had a similar situation and have very little knowledge of SQL functions however needed to strip RTF and tried this code. Debugging suggested that this function was failing was here as I was getting Invalid length parameter.
IF RIGHT(#rtf, 1) = ' '
SET #rtf = SUBSTRING(#rtf, 1, LEN(#rtf) -1);
As I have minimal knowledge and lack of time I just added a second if to make sure it wasn't doing a subtraction i.e. -1 from 0 which worked for my dataset.
IF RIGHT(#rtf, 1) = ' '
IF LEN(#rtf) > 0
SET #rtf = SUBSTRING(#rtf, 1, LEN(#rtf) -1);

Replace Value in Custom format for MS SQL

I have a set of data for example:
Part no Custom Format
1128005 \Machines\3D\PartNo(2)\PartNo(4)xx\PartNo(7)
11.88.006 \Machines\3D\PartNo(2)\PartNo+3(2)xx\PartNo+6(3)
I want to replace the variable set in the custom format define in it. The result i am looking for is
For Part no
1128005
the result is
\Machines\3D\11\1128xx\1128005
11.88.006
\Machines\3D\11\88xx\006
Any ideas?
Thanks
Regards
If my understanding is correct, below script should be what you want? But because it processes row by row, it can be very slow when dealing with millions of rows.
--declare #input varchar(50) = '11.88.006', #pattern varchar(255) = '\Machines\3D\PartNo(2)\PartNo+3(2)xx\PartNo+6(3)'
declare #input varchar(50) = '1128005', #pattern varchar(255) = '\Machines\3D\PartNo(2)\PartNo(4)xx\PartNo(7)'
declare #tblPattern table (tmpKey int identity(1,1), result varchar(50), Position varchar(50), SkipCnt int, CharLength int, Sufix varchar(50))
declare #i int = 1, #output varchar(max) = '\Machines\3D\'
Declare #PatternXml XML
Set #PatternXml = N'<root><r>' + REPLACE(replace(#pattern, '\Machines\3D\', ''), '\', '</r><r>') + '</r></root>'
insert into #tblPattern(result)
select r.value('.', 'VARCHAR(MAX)') as t
from #PatternXml.nodes('//root//r') as records(r)
update #tblPattern set Position = REPLACE(result, 'PartNo', '')
, SkipCnt = CASE WHEN CHARINDEX('+', result, 1) > 0 THEN SUBSTRING(result, CHARINDEX('+', result, 1) + 1, CHARINDEX('(', result, 1) - CHARINDEX('+', result, 1) - 1) ELSE 0 END
, CharLength = SUBSTRING(result, CHARINDEX('(', result, 1) + 1, CHARINDEX(')', result, 1) - CHARINDEX('(', result, 1) - 1 )
, Sufix = SUBSTRING(result, CHARINDEX(')', result, 1) + 1, LEN(result))
while #i <= 3
begin
select #output += SUBSTRING(#input, 1 + SkipCnt, CharLength) + Sufix + '\'
from #tblPattern
where tmpKey = #i
set #i += 1
end
select #output = STUFF(#output, len(#output), 1, '')
select #output

Splitting a dynamic string

I need help parsing a dynamic string(see below). This string can change to only have one set of values to two, to three(like the one below).
Raw String:
valueA=valueB=valueC=valueD==valueE&valueA=valueB=valueC=valueD==valueE&valueA=valueB=valueC=valueD==valueE
End Result: VelueB, ValueB, ValueB
I have been able to extract valueA using the STUFF and CHARINDEX function(see below) but I'm having difficulty just getting valueB. I have also tried doing it in ssrs using the split function but getting nowhere.
STUFF(( SELECT ',' + ' ' + SUBSTRING(param, 1,
CHARINDEX('=', param) - 1)
FROM dbo.Fn_mvparam(column_a, '&') AS fm
FOR
XML PATH('')
), 1, 1, ' ')
This should do the trick for you. Hope its useful to you
CREATE FUNCTION uft_DoubleSplitter
(
-- Add the parameters for the function here
#String VARCHAR(4000),
#Splitter1 CHAR,
#Splitter2 CHAR
)
RETURNS #Result TABLE (Id INT,MId INT,SValue VARCHAR(4000))
AS
BEGIN
DECLARE #FResult TABLE(Id INT IDENTITY(1, 1),
SValue VARCHAR(4000))
DECLARE #SResult TABLE(Id INT IDENTITY(1, 1),
MId INT,
SValue VARCHAR(4000))
SET #String = #String+#Splitter1
WHILE CHARINDEX(#Splitter1, #String) > 0
BEGIN
DECLARE #WorkingString VARCHAR(4000) = NULL
SET #WorkingString = SUBSTRING(#String, 1, CHARINDEX(#Splitter1, #String) - 1)
--Print #workingString
INSERT INTO #FResult
SELECT CASE
WHEN #WorkingString = '' THEN NULL
ELSE #WorkingString
END
SET #String = SUBSTRING(#String, LEN(#WorkingString) + 2, LEN(#String))
END
IF ISNULL(#Splitter2, '') != ''
BEGIN
DECLARE #OStartLoop INT
DECLARE #OEndLoop INT
SELECT #OStartLoop = MIN(Id),
#OEndLoop = MAX(Id)
FROM #FResult
WHILE #OStartLoop <= #OEndLoop
BEGIN
DECLARE #iString VARCHAR(4000)
DECLARE #iMId INT
SELECT #iString = SValue+#Splitter2,
#iMId = Id
FROM #FResult
WHERE Id = #OStartLoop
WHILE CHARINDEX(#Splitter2, #iString) > 0
BEGIN
DECLARE #iWorkingString VARCHAR(4000) = NULL
SET #IWorkingString = SUBSTRING(#iString, 1, CHARINDEX(#Splitter2, #iString) - 1)
INSERT INTO #SResult
SELECT #iMId,
CASE
WHEN #iWorkingString = '' THEN NULL
ELSE #iWorkingString
END
SET #iString = SUBSTRING(#iString, LEN(#iWorkingString) + 2, LEN(#iString))
END
SET #OStartLoop = #OStartLoop + 1
END
INSERT INTO #Result
SELECT MId AS PrimarySplitID,
ROW_NUMBER() OVER (PARTITION BY MId ORDER BY Mid, Id) AS SecondarySplitID ,
SValue
FROM #SResult
END
ELSE
BEGIN
INSERT INTO #Result
SELECT Id AS PrimarySplitID,
NULL AS SecondarySplitID,
SValue
FROM #FResult
END
RETURN
Usage:
--FirstSplit
SELECT * FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===','&',NULL)
--Second Split
SELECT * FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===','&','=')
Scenario Answer:
SELECT fn.SValue
FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===', '&', '=')AS fn
WHERE fn.mid = 2
This should do it:
REPLACE(#Param, "=", ", ")

Split and Replace the string in SqlServer 2008?

I want to split string 'GPIN-KH2-COH-24042014-02' by '-' in sqlserver 2008 and want to save in separate variables. How can I achieve this? Please help me in this regard.
Now I am using below function and pass it ('GPIN-KH2-COH-24042014-02', '-')
Create FUNCTION [dbo].[fnSplit]
(
#strInputList NVARCHAR (MAX), -- List of Delimited Items
#strDelimiter NVARCHAR (11) = ',' -- Delimiter that Separates Items
)
RETURNS #tblList TABLE (strItem NVARCHAR(250))
BEGIN
DECLARE #strItem NVARCHAR(MAX)
WHILE CHARINDEX(#strDelimiter,#strInputList,0) <> 0
BEGIN
SELECT
#strItem = RTRIM(LTRIM( (SUBSTRING (#strInputList, 1, CHARINDEX (#strDelimiter, #strInputList, 0) -1)))),
#strInputList = RTRIM(LTRIM( (SUBSTRING (#strInputList, CHARINDEX (#strDelimiter, #strInputList, 0) + LEN (#strDelimiter), LEN (#strInputList)))))
IF LEN(#strItem) > 0
INSERT INTO #tblList SELECT #strItem
END
IF LEN(#strInputList) > 0
INSERT INTO #tblList SELECT #strInputList
RETURN
END
Result:
strItem
GPIN
KH2
COH
24042014
02
When I was delimeter and string it return result in that table format as shown above. But I want to get last two rows. How can I get this?
CRETAE function and pass your string and Delimiter as parameter
FUNCTION:
CREATE FUNCTION [dbo].[Split](#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
And then call the function and use the values as you want
DECLARE #A VARCHAR (100)= 'GPIN-KH2-COH-24042014-02'
SELECT items
INTO #STRINGS
FROM dbo.split(#A,'-')
select * from #STRINGS
Use this function, download from here
usage
SELECT DBO.fnString_DelimeterIndex(N'GPIN-KH2-COH-24042014-02','-',1)
SELECT DBO.fnString_DelimeterIndex(N'GPIN-KH2-COH-24042014-02','-',2)
SELECT DBO.fnString_DelimeterIndex(N'GPIN-KH2-COH-24042014-02','-',3)
function
ALTER FUNCTION [dbo].[fnString_DelimeterIndex]
(
#Text NVARCHAR(4000),
#Delimiter CHAR,
#Section SMALLINT
)
RETURNS NVARCHAR(4000)
AS
BEGIN
DECLARE #NextPos SMALLINT,
#LastPos SMALLINT,
#Found SMALLINT,
#REVERSE BIT
IF #Section < 0
SELECT #Text = REVERSE(#Text)--, #Section=1,#REVERSE=1
SELECT #NextPos = CHARINDEX(#Delimiter, #Text, 1),
#LastPos = 0,
#Found = 1
WHILE #NextPos > 0 AND ABS(#Section) <> #Found
SELECT #LastPos = #NextPos,
#NextPos = CHARINDEX(#Delimiter, #Text, #NextPos + 1),
#Found = #Found + 1
RETURN CASE
WHEN #Found <> ABS(#Section) OR #Section = 0 THEN NULL
--WHEN #REVERSE =1 THEN
WHEN #Section > 0 THEN SUBSTRING(#Text, #LastPos + 1, CASE WHEN #NextPos = 0 THEN DATALENGTH(#Text) - #LastPos ELSE #NextPos - #LastPos - 1 END)
ELSE REVERSE(SUBSTRING(#Text, #LastPos + 1, CASE WHEN #NextPos = 0 THEN DATALENGTH(#Text) - #LastPos ELSE #NextPos - #LastPos - 1 END))
END
END
Here is a fancy way of solving it:
You need a function to split it first:
create function [dbo].[f_specialsplit]
(
#param nvarchar(max),
#delimiter char(1)
)
returns #t table (val nvarchar(max), rn varchar(9))
as
begin
set #param += #delimiter
;with a as
(
select cast(1 as bigint) f, charindex(#delimiter, #param) t where #param is not null
union all
select t + 1, charindex(#delimiter, #param, t + 1)
from a
where charindex(#delimiter, #param, t + 1) > 0
)
insert #t
select substring(#param, f, t - f), row_number() over (order by (select 1)) from a
option (maxrecursion 0)
return
end
Trick is now to pivot the data into the variables:
DECLARE #str varchar(100) = 'GPIN-KH2-COH-24042014-02'
DECLARE #s1 varchar(100),#s2 varchar(100),#s3 varchar(100),#s4 varchar(100),#s5 varchar(100)
SELECT #s1=[1],#s2=[2],#s3=[3],#s4=[4],#s5=[5]
FROM f_specialsplit(#str,'-')
PIVOT
(min([val])
FOR rn
in([1],[2],[3],[4],[5])
)AS p
SELECT #s1,#s2,#s3,#s4,#s5
Result:
GPIN KH2 COH 24042014 02

how to parse an unknown length string in sql server 2012

How to parse unknown length string into different columns by using delimiter ('.').
declare osversion varchar(100)
set osversion = '6.2.9295'
SELECT [Part1] = LEFT(osversion,CHARINDEX('.',osversion) - 1),
[Part2] = SUBSTRING(osversion,CHARINDEX('.',osversion) + 1,
CHARINDEX('.',osversion,CHARINDEX('.',
osversion) + 1) - (CHARINDEX('.',osversion) + 1)),
[Part3] = SUBSTRING(osversion,CHARINDEX('.',
osversion,CHARINDEX('.',osversion) + 1) + 1,
DATALENGTH(osversion) - CHARINDEX('.',
osversion,CHARINDEX('.',osversion) + 1) -
CHARINDEX('.',REVERSE(osversion))),
from table1
Result:
Part1 Part2 Part3
6 2 9295
This result is for fixed length of string. I want to parse for an unknown length of string.
Like '86.52.929.695.22.1234'. Please help.
I purpose you to recreate the C#'s string.Split function in SQL. Here is the code
CREATE FUNCTION [dbo].[f_Split](
#String NVARCHAR (4000),
#Delimiter NVARCHAR (10)
)
RETURNS #T TABLE ([Value] NVARCHAR(4000))
BEGIN
DECLARE #NEXTSTRING NVARCHAR(4000)
DECLARE #POS INT,#DELIM_SIZE INT
DECLARE #NEXTPOS INT
SELECT
#NEXTSTRING = '',
#String = #String + #Delimiter,
#DELIM_SIZE = LEN(#Delimiter)
SET #POS = CHARINDEX(#Delimiter,#String)
SET #NEXTPOS = 1
WHILE (#POS <> 0)
BEGIN
SET #NEXTSTRING = SUBSTRING(#String,1,#POS - 1)
INSERT INTO #T ( [VALUE]) VALUES (#NEXTSTRING)
SET #String = SUBSTRING(#String,#POS +#DELIM_SIZE,LEN(#String))
SET #NEXTPOS = #POS
SET #POS = CHARINDEX(#Delimiter,#String)
END
RETURN
END
You can call the function like that
DECLARE #temp nvarchar(255); SELECT #temp = '86.52.929.695.22.1234';
SELECT * FROM dbo.f_Split(#temp,'.');
Here is the output :
Value
86
52
929
695
22
1234

Resources