sql query to get word between characters - sql-server-2005

I have problem where I have a string and I need to get a specific part of it.
For example:
\Stack\Over\Programming\Users\
I need "Programming" from the above string.

DECLARE #TExt NVARCHAR(MAX)= '\Stack\Over\Programming\Users\'
DECLARE #Delimiter VARCHAR(1000)= '\' ;
WITH numbers
AS ( SELECT ROW_NUMBER() OVER ( ORDER BY o.object_id, o2.object_id ) Number
FROM sys.objects o
CROSS JOIN sys.objects o2
),
c AS ( SELECT Number CHARBegin ,
ROW_NUMBER() OVER ( ORDER BY number ) RN
FROM numbers
WHERE SUBSTRING(#text, Number, LEN(#Delimiter)) = #Delimiter
),
res
AS ( SELECT CHARBegin ,
CAST(LEFT(#text, charbegin) AS NVARCHAR(MAX)) Res ,
RN
FROM c
WHERE rn = 1
UNION ALL
SELECT c.CHARBegin ,
CAST(SUBSTRING(#text, res.CHARBegin+1,
c.CHARBegin - res.CHARBegin-1) AS NVARCHAR(MAX)) ,
c.RN
FROM c
JOIN res ON c.RN = res.RN + 1
)
SELECT *
FROM res
Result:
CHARBegin |Res |RN
1 | \ |1
7 |Stack |2
12 |Over |3
24 |Programming |4
30 |Users |5
In your case you need last statement
SELECT * FROM res WHERE Rn=4

If it is always the word between the 3th and 4th \, following would do the trick.
DECLARE #String VARCHAR(32)
SET #String = '\Stack\Over\Programming\Users\'
SELECT SUBSTRING(
#String
, CHARINDEX('\', #String, CHARINDEX('\', #String, CHARINDEX('\', #String, 1) + 1) + 1) + 1
, CHARINDEX('\', #String, CHARINDEX('\', #String, CHARINDEX('\', #String, CHARINDEX('\', #String, 1) + 1) + 1) + 1)
- CHARINDEX('\', #String, CHARINDEX('\', #String, CHARINDEX('\', #String, 1) + 1) + 1) - 1)

you will need to create a string split function in SQL. unfortunately there is not one built into MS SQL.
How do I split a string so I can access item x?

Related

Split a string in to columns

I would like to split below string:
#string = '?Reqid=325235&step=5&substep=13'
Desired output:
String Key step substep
?Reqid=325235&step=5&substep=13 325235 5 13
Edited:
So far I tried
declare #string varchar(100) = '?Reqid=325235&step=5&substep=13'
select #string String,
substring(#string, len('?reqID=') + 1, (CHARINDEX('&step=', #string) - (len('?reqID=') + 1))) key,
0 Step,
0 SubStep
I didn't get a way to find for rest of columns
Current results:
String Key step substep
?Reqid=325235&step=5&substep=13 325235 0 0
SQL-Server is not well suited for this task, but there is a very nice work around using either JSON (version 2016+) or XML (starting form v2005):
DECLARE #string VARCHAR(100) = '?Reqid=325235&step=5&substep=13';
--The JSON approach (needs v2016+)
SELECT *
FROM OPENJSON(CONCAT('{"',REPLACE(REPLACE(STUFF(#string,1,1,''),'&','","'),'=','":"'),'"}'))
WITH(Reqid INT
,step INT
,substep INT);
--The XML approach (for older versions)
SELECT A.CastedToXml.value('(x/#Reqid)[1]','int') As Reqid
,A.CastedToXml.value('(x/#step)[1]','int') As step
,A.CastedToXml.value('(x/#substep)[1]','int') As substep
FROM (SELECT CAST('<x ' + REPLACE(REPLACE(STUFF(#string,1,1,''),'=','="'),'&','" ') + '" />' AS XML)) A(CastedToXml);
The idea in short:
Using some simpe string operations we can transform your string in JSON
{"Reqid":"325235","step":"5","substep":"13"}
or XML
<x Reqid="325235" step="5" substep="13" />
Reading JSON is done with OPENJSON in connection with a WITH-clause (implicit pivoting). Reading XML's attributes is done using the XML's type method .value().
SQL Server really lacks regex functions, and this type of string manipulation would probably be better handled on application side.
That being said, assuming that the keys are always in the same sequence, you could do:
select
substring(
#string,
charindex('Reqid=', #string) + len('Reqid='),
charindex('&', #string)
- charindex('Reqid=', #string)
- len('Reqid=')
) [key],
substring(
#string,
charindex('step=', #string) + len('step='),
charindex('&', #string, charindex('step=', #string) + len('step='))
- charindex('step=', #string)
- len('step=')
) step,
right(
#string,
len(#string) - charindex('substep=', #string) - len('substep=') + 1
) substep
Demo on DB Fiddle:
key | step | substep
:----- | :--- | :------
325235 | 5 | 13
Assuming a fixed format for your string (URL) then the following pulls out the info requested:
declare #string varchar(100) = '?Reqid=325235&step=5&substep=13';
select
substring(#string, End1+1, Start2-End1-1) ReqID
, substring(#string, End2+1, Start3-End2-1) Step
, substring(#string, End3+1, EndEnd-End3-1) SubStep
from (
select #string String
, charindex('?reqID=', #string) Start1
, charindex('?reqID=', #string) + 6 End1
, charindex('&step=', #string) Start2
, charindex('&step=', #string) + 5 End2
, charindex('&substep=', #string) Start3
, charindex('&substep=', #string) + 8 End3
, len(#string)+1 EndEnd
) X;
Result:
ReqID Step SubStep
325235 5 13
This may help
select #string String,
substring(#string, len('?reqID=') + 1, (CHARINDEX('&step=', #string) - (len('?reqID=') + 1))) [Key],
SUBSTRING(#string, CHARINDEX('&step=', #string) + len('&step='), CHARINDEX('&substep', #string) - CHARINDEX('&step=', #string) - len('&step=')) Step,
SUBSTRING(#string, CHARINDEX('&substep=', #string) + len('&substep='), len(#string) - charindex('&substep=', #string)) Step
This is a pretty simple option that should be easy to understand:
DECLARE #string varchar(max) = '?Reqid=325235&step=5&substep=13';
DECLARE #RemainingString varchar(max) = SUBSTRING(#string, 8, LEN(#string));
DECLARE #AmpersandLocation int = CHARINDEX('&', #RemainingString);
DECLARE #Key varchar(100) = SUBSTRING(#RemainingString, 1, #AmpersandLocation - 1);
SET #RemainingString = SUBSTRING(#RemainingString, LEN(#Key) + 7, LEN(#RemainingString));
SET #AmpersandLocation = CHARINDEX('&', #RemainingString);
DECLARE #Step varchar(100) = SUBSTRING(#RemainingString, 1, #AmpersandLocation - 1);
DECLARE #Substep varchar(100) = SUBSTRING(#RemainingString, LEN(#Step) + 10, LEN(#RemainingString));
SELECT #string AS [String], #Key AS [Key], #Step AS [Step], #Substep AS Substep;
If you are using SQL Server 2016 or later, you can use STRING_SPLIT function to split the string by a character. See the full query below.
declare #string varchar(100) = '?Reqid=325235&step=5&substep=13', #c nchar(1) = N'='
SELECT #string AS [String], [1] AS [Key], [2] AS [step], [3] AS [substep]
FROM
(
SELECT TOP 100 PERCENT
Val = substring(value, 0, charindex('&', value)),
R = ROW_NUMBER() OVER ( ORDER BY CHARINDEX(#c + value + #c, #c + #string + #c) )
FROM STRING_SPLIT(#string + '&', #c)
WHERE RTRIM(value) '?Reqid'
ORDER BY R
)
AS SourceTable
PIVOT ( MAX(Val) FOR R IN ([1], [2], [3]) ) AS PivotTable;
Demo on SQL Fiddle
+---------------------------------+-----+--------+---------+
| String | Key | step | substep |
+---------------------------------+-----+--------+---------+
| ?Reqid=325235&step=5&substep=13 | 13 | 325235 | 5 |
+---------------------------------+-----+--------+---------+
Update: as STRING_SPLIT doesn't guarantee the order of results, I have used the ROW_NUMBER() function to make the order consistent. The character index of = is used for this purpose.

How to extract URL querystring parameters in SQL Server without writing a function?

I'm having trouble identifying all the querystring parameters that are used on a site. I want to write a T-SQL query that extracts all parameters and counts them, but I don't have permission to write SQL functions, so this solution isn't much help.
The field that I'm working with (Query) includes data that looks like this:
_=1457999955221
tab=profile
tab=tags&sort=votes&page=13
page=5&sort=newest&pagesize=15
...
The query I need to write would return the result:
querystring | count
___________________
_ | 1
tab | 2
sort | 2
page | 2
pagesize | 1
...
Any help is greatly appreciated.
You can borrow one of the functions from here and just inline it into the query.
An example below. I would not expect good performance. Creating a CLR function is by far the most efficient way of splitting strings prior to SQL Server 2016.
DECLARE #QueryStrings Table
(
Query VARCHAR(8000)
)
INSERT INTO #QueryStrings
VALUES
('INVALID'),
('_=1457999955221'),
('tab=profile'),
('tab=tags&sort=votes&page=13'),
('page=5&sort=newest&pagesize=15');
WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b)
SELECT parameter, count(*)
FROM #QueryStrings qs
CROSS APPLY (SELECT SUBSTRING(qs.Query, t.N + 1, ISNULL(NULLIF(CHARINDEX('&', qs.Query, t.N + 1), 0) - t.N - 1, 8000))
FROM (SELECT 0
UNION ALL
SELECT TOP (DATALENGTH(ISNULL(qs.Query, 1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM E42) t(N)
WHERE ( SUBSTRING(qs.Query, t.N, 1) = '&'
OR t.N = 0 )) ca1(split_result)
CROSS APPLY (SELECT CHARINDEX('=',split_result)) ca2(pos)
CROSS APPLY (SELECT CASE WHEN pos > 0 THEN LEFT(split_result,pos-1) END,
CASE WHEN pos > 0 THEN SUBSTRING(split_result, pos+1,8000) END
WHERE pos > 0) ca3(parameter,value)
GROUP BY parameter
More sexy way to approach that:
DECLARE #xml xml
;WITH cte AS (
SELECT *
FROM (VALUES
('_=1457999955221'),
('tab=profile'),
('tab=tags&sort=votes&page=13'),
('page=5&sort=newest&pagesize=15')
) as T(Query))
SELECT #xml = (
SELECT CAST(
(
SELECT '<d><param>' + REPLACE(REPLACE((STUFF((
SELECT '/' + REPLACE(REPLACE(Query,'&','/'),'=','!')
FROM cte
FOR XML PATH('')
),1,1,'')),'/','</value><param>'),'!','</param><value>') + '</value></d>') as xml))
;WITH final AS (
SELECT t.v.value('.','nvarchar(20)') as querystring
FROM #xml.nodes('/d/param') as t(v)
)
SELECT querystring, COUNT(*) as [count]
FROM final
GROUP BY querystring
Result:
querystring count
-------------------- -----------
_ 1
page 2
pagesize 1
sort 2
tab 2
(5 row(s) affected)
Necromancing.
This can now be done easily with SQL Server 2016+ (13.x+)
-- Required for STRING_SPLIT:
-- ALTER DATABASE <db_name> SET COMPATIBILITY_LEVEL = 130 -- >= 130
BEGIN TRY
DECLARE #sql nvarchar(MAX);
-- https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-database-transact-sql-compatibility-level?view=sql-server-ver15
-- SET #sql = N'ALTER DATABASE [COR_Basic_Demo_V4] SET COMPATIBILITY_LEVEL = 130; ';
SET #sql = N'ALTER DATABASE ' + QUOTENAME(DB_NAME()) + N' SET COMPATIBILITY_LEVEL = ' + (SELECT CAST(MAX(compatibility_level) AS nvarchar(10)) FROM sys.databases) + '; ';
-- PRINT #sql;
EXECUTE(#sql);
END TRY
BEGIN CATCH
-- Execute error retrieval routine.
-- EXECUTE usp_GetErrorInfo;
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
;
END CATCH
-- Here comes the actual computation
DECLARE #input nvarchar(4000)
SET #input = N'_=1457999955221
tab=profile
tab=tags&sort=votes&page=13
page=5&sort=newest&pagesize=15'
;WITH CTE AS
(
SELECT
value
,SUBSTRING(splitted.value, 1, NULLIF(CHARINDEX('=', splitted.value), 0) -1) AS k
,SUBSTRING(splitted.value, NULLIF(CHARINDEX('=', splitted.value), 0) + 1, LEN(splitted.value)) AS v
FROM STRING_SPLIT
(
REPLACE
(
REPLACE(#input, CHAR(13), '')
,CHAR(10)
,'&'
)
, '&'
) AS splitted
)
SELECT
k
,COUNT(v) AS cnt
,COUNT(DISTINCT v) AS dist_cnt
FROM CTE
GROUP BY k
For earlier versions, or if you actually need to decompose a full-url:
DECLARE #String nvarchar(4000)
DECLARE #path nvarchar(MAX)
DECLARE #hash nvarchar(MAX)
DECLARE #Delimiter nchar(1)
SET #String = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
SET #String = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET #String = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
SET #Delimiter = '&'
SELECT
#path = SUBSTRING(#String, 1, NULLIF(CHARINDEX('?', #String) - 1, -1)) -- AS path
,#hash = RIGHT(#String, LEN(#String) - NULLIF(CHARINDEX(N'#', #String), 0) ) -- AS hash
SELECT -- remove hash
#String = SUBSTRING
(
#String
,1
,COALESCE(NULLIF(CHARINDEX(N'#', #String), 0) - 1, LEN(#String) )
) -- AS xxx
;
SELECT -- remove path
#String = SUBSTRING
( #String
,CHARINDEX(N'?', #String) + 1
,100000
)
;
;WITH Split(id, stpos, endpos, data)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(#Delimiter, #String) AS endpos
,SUBSTRING(#String, 0, COALESCE(NULLIF(CHARINDEX(#Delimiter, #String), 0), LEN(#String)+1) ) AS data
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(#Delimiter, #String, Split.endpos+1) AS endpos
,SUBSTRING(#String, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(#Delimiter, #String, Split.endpos+1), 0), LEN(#String)+1) - Split.endpos - 1) AS data
FROM Split
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,SUBSTRING(#String, stpos, COALESCE(NULLIF(endpos, 0), LEN(#String)+1) - stpos) AS data_simple
,data
,#path AS path
,#hash AS hash
,SUBSTRING(data, 1, NULLIF(charindex('=', data), 0) -1) AS k
,SUBSTRING(data, NULLIF(charindex('=', data), 0) + 1, LEN(data)) AS v
FROM Split
And if you want a function:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tfu_DecomposeUrl]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
EXECUTE('CREATE FUNCTION dbo.tfu_DecomposeUrl( ) RETURNS TABLE AS RETURN ( SELECT 123 AS abc) ');
GO
ALTER FUNCTION dbo.tfu_DecomposeUrl
(
#input_string NVARCHAR(4000)
)
RETURNS TABLE
AS
RETURN
(
WITH CTE AS
(
SELECT
SUBSTRING(#input_string, 1, NULLIF(CHARINDEX('?', #input_string) - 1, -1)) AS query_path
,RIGHT(#input_string, LEN(#input_string) - NULLIF(CHARINDEX(N'#', #input_string), 0) ) AS query_hash
,SUBSTRING
(
#input_string
,1
,COALESCE(NULLIF(CHARINDEX(N'#', #input_string), 0) - 1, LEN(#input_string) )
) AS PathWithoutHash
)
,CTE2 AS
(
SELECT
CTE.query_path
,CTE.query_hash
,SUBSTRING
( PathWithoutHash
,CHARINDEX(N'?', PathWithoutHash) + 1
,100000
) AS KeyValueString
FROM CTE
)
,Split(id, stpos, endpos, data, query_path, query_hash)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(N'&', CTE2.KeyValueString) AS endpos
,SUBSTRING(CTE2.KeyValueString, 0, COALESCE(NULLIF(CHARINDEX(N'&', CTE2.KeyValueString), 0), LEN(CTE2.KeyValueString)+1) ) AS data
,CTE2.query_path
,CTE2.query_hash
FROM CTE2
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(N'&', CTE2.KeyValueString, Split.endpos+1) AS endpos
,SUBSTRING(CTE2.KeyValueString, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(N'&', CTE2.KeyValueString, Split.endpos+1), 0), LEN(CTE2.KeyValueString)+1) - Split.endpos - 1) AS data
,CTE2.query_path
,CTE2.query_hash
FROM Split
CROSS JOIN CTE2
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,SUBSTRING(#String, stpos, COALESCE(NULLIF(endpos, 0), LEN(#String)+1) - stpos) AS data_simple
,data
,query_path
,query_hash
,SUBSTRING(data, 1, NULLIF(CHARINDEX('=', data), 0) -1) AS k
,SUBSTRING(data, NULLIF(CHARINDEX('=', data), 0) + 1, LEN(data)) AS v
FROM Split
)
GO
Which can be simplified into this
DECLARE #input_string nvarchar(4000)
SET #input_string = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET #input_string = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET #input_string = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
-- SET #input_string = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
;WITH CTE AS
(
SELECT
SUBSTRING(#input_string, 1, NULLIF(CHARINDEX('?', #input_string) - 1, -1)) AS query_path
,RIGHT(#input_string, LEN(#input_string) - NULLIF(CHARINDEX(N'#', #input_string), 0) ) AS query_hash
,SUBSTRING
(
#input_string
,1
,COALESCE(NULLIF(CHARINDEX(N'#', #input_string), 0) - 1, LEN(#input_string) )
) AS PathWithoutHash
)
,CTE2 AS
(
SELECT
CTE.query_path
,CTE.query_hash
,SUBSTRING
( PathWithoutHash
,CHARINDEX(N'?', PathWithoutHash) + 1
,100000
) AS KeyValueString
FROM CTE
)
SELECT
t.id
,t.data
,CTE2.query_path
,CTE2.query_hash
,SUBSTRING(t.data, 1, NULLIF(CHARINDEX('=', t.data), 0) -1) AS k
,SUBSTRING(t.data, NULLIF(CHARINDEX('=', t.data), 0) + 1, LEN(t.data)) AS v
FROM CTE2
OUTER APPLY dbo.tfu_FastSplitString(CTE2.KeyValueString, N'&') AS t
And a table-valued string-splitting function:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.tfu_FastSplitString') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
EXECUTE('CREATE FUNCTION dbo.tfu_FastSplitString( ) RETURNS TABLE AS RETURN ( SELECT 123 AS abc) ');
GO
ALTER FUNCTION dbo.tfu_FastSplitString
(
#input_string nvarchar(4000)
,#delimiter nchar(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(id, stpos, endpos) -- , data)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(#delimiter, #input_string) AS endpos
-- ,SUBSTRING(#input_string, 0, COALESCE(NULLIF(CHARINDEX(#delimiter, #input_string), 0), LEN(#input_string)+1) ) AS data
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(#delimiter, #input_string, Split.endpos+1) AS endpos
-- ,SUBSTRING(#input_string, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(#delimiter, #input_string, Split.endpos+1), 0), LEN(#input_string)+1) - Split.endpos - 1) AS data
FROM Split
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,data
,SUBSTRING(#input_string, stpos, COALESCE(NULLIF(endpos, 0), LEN(#input_string)+1) - stpos) AS data
FROM Split
)
GO
These are inline table-valued functions, so they should be fast.
If it were a multi-statement table-valued function, it would be slow.

Hot to convert a variable with value '1,2,3' to a table (every number as a record)

Working on SQL (2005 and 2008)
the variable with value '1,2,3' would be call #cedis and this could to have N number for example
set #cedis='1' or set #cedis='1,2,3,4,5,6,7' or set #cedis='125,98,91'
so important, its this must to be a select only, a loop could not to be use, only a select!
this must to return a (result as ) table with values for example
set #cedis='1,2,3,4' this must to return a result
number 1 2 3 4
declare #cedis varchar(max)
set #cedis='1,58,123,8'
;with datos as
(
my select with is going to return me the table
)
select * from datos
result set is
number
1
58
123
8
If am not wrong this is what you need
DECLARE #cedis VARCHAR(500)='1,2,3,4'
SELECT Split.a.value('.', 'VARCHAR(100)') Numbers
FROM (SELECT Cast ('<M>' + Replace(#cedis, ',', '</M><M>') + '</M>' AS XML) AS Numbers) AS A
CROSS APPLY Numbers.nodes ('/M') AS Split(a)
Result:
Numbers
-------
1
2
3
4
A table valued function would do it.
CREATE FUNCTION [dbo].[fn_Split](#text VARCHAR(MAX), #delimiter VARCHAR(5) = ',')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value VARCHAR(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
END
SET #text = RIGHT(#text, (LEN(#text) - (#index+LEN(#delimiter)-1)))
END
RETURN
END
You can call it as follows:
SELECT *
FROM dbo.fn_Split(#cedis,',')
Here is a more generic solution that breaks any given string into a table based on any given separator:
http://rextester.com/VSRDLS48817
Not an original idea, but I've found it very useful.
create function [dbo].[SplitString]
(
#str nvarchar(255),
#separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
select
cast(1 as int),
cast(1 as int),
charindex(#separator, #str)
union all
select
p + 1,
b + 1,
charindex(#separator, #str, b + 1)
from tokens
where b > 0
)
select
p-1 ItemIndex,
substring(
#str,
a,
case when b > 0 then b-a ELSE LEN(#str) end)
AS Item
from tokens
);
This is another one approach to get required output result
DECLARE #cedis VARCHAR(MAX) ,
#delimeter VARCHAR(10)
SET #cedis = '1,58,123,8,14144,15,155231,15,3647,2347,45,76,68,2354,577,5'
SET #delimeter = ','
SET #cedis = #cedis + #delimeter;
WITH datos
AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM datos
WHERE n <= LEN(#cedis)
),
cte
AS ( SELECT T.N ,
ROW_NUMBER() OVER ( ORDER BY T.N ) AS RN
FROM datos AS T
WHERE SUBSTRING(#cedis, T.N, LEN(#delimeter)) = #delimeter
AND LEN(#cedis) >= T.N
)
SELECT SUBSTRING(#cedis, COALESCE(R.N + LEN(#delimeter), 1),
L.N - COALESCE(R.N + LEN(#delimeter), 1)) AS part ,
L.RN AS ID
FROM cte AS L
LEFT JOIN cte AS R ON L.RN = R.RN + 1
OPTION ( MAXRECURSION 1000 )

How to split dash-separated values in SQL Server?

How do I get only the middle part of the data in my table? I tried the following code, but this only removes the right part... my output should only be middle part.
For instance when I select the data 1-021514-1 the output should be 021514 without the left and right dashes
select LEFT(ticketid, CHARINDEX('-', ticketid + '-') + 4)
from Table
My Data is:
|TicketID |
------------
|1-021514-1 |
|10-021514-1|
|2-021514-1 |
|4-021414-1 |
Please try:
SELECT
LEFT(st, CHARINDEX('-', st)-1) TicketID
from
(
SELECT
SUBSTRING(TicketID, CHARINDEX('-',TicketID)+1, 10000) st
FROM Table
)x
Try this
with t as (select TicketID as val)
select t.*,
LEFT(val, charindex('-', val) - 1),
SUBSTRING(val, charindex('-', val)+1, len(val) - CHARINDEX('-', reverse(val)) - charindex('-', val)),
REVERSE(LEFT(reverse(val), charindex('-', reverse(val)) - 1))
from t;
(Or)
Use below Function
CREATE FUNCTION dbo.SplitStrings_CTE(#List nvarchar(max), #Delimiter nvarchar(1))
RETURNS #returns TABLE(val nvarchar(max), [level] int, PRIMARY KEY CLUSTERED([level]))
AS
BEGIN
;WITH cte AS
(
SELECT SUBSTRING(#List, 0, CHARINDEX(#Delimiter, #List)) AS val,
CAST(STUFF (#List + #Delimiter, 1, CHARINDEX(#Delimiter, #List), '') AS nvarchar(max)) AS stval,
1 AS [level]
UNION ALL
SELECT SUBSTRING(stval, 0, CHARINDEX(#Delimiter, stval)),
CAST(STUFF (stval, 1, CHARINDEX(#Delimiter, stval), '') AS nvarchar(max)),
[level] + 1
FROM cte
WHERE stval != ''
)
INSERT #returns
SELECT REPLACE(val, ' ', '') AS val, [level]
FROM cte
RETURN
END
hi Ron try this,
declare #string varchar(25)='1-021514-1'
declare #val varchar(25)
SELECT #val= SUBSTRING(#string, CHARINDEX('-', #string)+1, ((CHARINDEX('-',#string,(charindex('-',#string)+1))-CHARINDEX('-', #string))-1))
select #val
Try this:
select right(left(ticketid, charindex('-', ticketid, charindex('-', ticketid, 0) + 1) - 1), len(left(ticketid, charindex('-', ticketid, charindex('-', ticketid, 0) + 1) - 1)) - charindex('-', left(ticketid, charindex('-', ticketid, charindex('-', ticketid, 0) + 1) - 1), 0)) from Table
Try this
SELECT STUFF(
STUFF(TicketID,1,CHARINDEX('-',TicketID,1),'')
,CHARINDEX('-',STUFF(TicketID,1,CHARINDEX('-',TicketID,1),''),1)
,LEN(TicketID)
,'')
from Table1

How can I separate dynamic data (different lengths) with a substring?

I need some assistance on separating data based on dynamic ranges with a start and end point.
For example, a string that looks like this:
N=Chris,,Lane,,M,,
N=Alen,,Smith,,E,,
N= is static and always the same. I can start a substring after the equals. The name on the other hand is dynamic and always changing in length, BUT there is a terminator with the commas. How do I select each segment of this column with SQL where Chris, Lane and M can be displayed in separate columns for First, Last and Middle name?
Adapting this example
SELECT p.id, REPLACE(p.[1], 'N=', '') Col1, p.[2] Col2, p.[3] Col3, p.[4] Col4, p.[5] Col5, p.[6] Col6, p.[7] Col7
FROM (
SELECT id, substring(d, start + 2, endPos - Start - 2) token, row_number() OVER (
PARTITION BY id ORDER BY start
) n
FROM (
SELECT id, d, n start, charindex(',', d, n + 2) endPos
FROM num
CROSS JOIN (
SELECT id, ',' + d + ',' d
FROM #m
) m
WHERE n < len(d) - 1
AND substring(d, n + 1, 1) = ','
) d
) pvt
Pivot(max(token) FOR n IN ([1], [2], [3], [4], [5], [6], [7])) p
demo
It's not pretty, but you can do parsing like this with charindex and locate:
select
substring(text, 3, charindex(',', text) - 3) firstName,
substring(text, charindex(',', text, charindex(',', text) + 1) + 1, charindex(',', text, charindex(',', text, charindex(',', text) + 1) + 1) - charindex(',', text, charindex(',', text) + 1) - 1) lastName,
substring(text, charindex(',', text, charindex(',', text, charindex(',', text, charindex(',', text) + 1) + 1) + 1) + 1, charindex(',', text, charindex(',', text, charindex(',', text, charindex(',', text, charindex(',', text) + 1) + 1) + 1) + 1) - charindex(',', text, charindex(',', text, charindex(',', text, charindex(',', text) + 1) + 1) + 1) - 1) middleName,
from
t
SQLFiddle here
Sounds like a job for.... split function!!! fn_split()
http://technet.microsoft.com/en-us/library/aa496058(v=sql.80).aspx
CREATE FUNCTION dbo.fn_Split(#text varchar(8000), #delimiter varchar(20) = ' ')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value varchar(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index >= 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
GO
SELECT * FROM dbo.fn_Split('N=Chris,,,,,Lane,,M,,',',')
or you can pivot to your liking....
SELECT [1] AS FirstName,[2],[3] AS LastName,[4],[5] AS MiddleInitial,[6]
FROM (
SELECT POSITION, VALUE
FROM dbo.fn_Split('N=Chris,,Lane,,M,,',',')
) p
PIVOT (MAX(VALUE) FOR POSITION IN ([1],[2],[3],[4],[5],[6])) AS pvt
Didn't want to do all the work but the downvote upset me... I don't know why it feel so personal when I'm downvoted.... I need to work on that. =(