Select substring up until second underscore in SQL - sql

I have strings like this:
FOO_BAR_3423423_dsadsa.doc
BAZ_BAZzz_dsadsa.nox
The number if _ can differ, but I need to select always up untill the second _:
FOO_BAR
BAZzz_BAZ
I am able to select until the first _:
SUBSTRING ([COLUMN], 0, CHARINDEX('/', [COLUMN], 0))
But how to expand this to go up to the second underscore?

You can use the Cross Apply the following query will give you the indexes with the data.
declare #T table
(
Name_Level varchar(25)
)
insert into #T values
('FOO_BAR_3423423_dsadsa'),
('BAZ_BAZzz_dsadsa'),
select SUBSTRING(Name_Level,0,p2.pos) As Data,p1.Pos as firstOccurance,p2.pos As secondOccurance
from #T
cross apply (select (charindex('_', Name_Level))) as P1(Pos)
cross apply (select (charindex('_', Name_Level, P1.Pos+1))) as P2(Pos)
Result:
-------------------------
FOO_BAR 4 8
BAZ_BAZzz 4 10
If you want to get the next index just add one more CrossApply and it will give you the next index of _.

Something like:
SUBSTRING ([COLUMN], 0, CHARINDEX('/', [COLUMN], CHARINDEX('/', [COLUMN], 0) + 1))
This won't work if there is less than two though.

Use while loop. Try this.
DECLARE #str VARCHAR(5000)='FOO_BAR_3423423_dsadsa.doc',
#temp INT=1,
#cnt INT=0,
#result VARCHAR(5000)='',
#intr VARCHAR(50)=''
WHILE #temp <= Len(#str)
BEGIN
SELECT #intr = Substring(#str, #temp, 1)
SET #cnt += (SELECT CASE WHEN #intr = '_' THEN 1 ELSE 0 END)
SET #result+=#intr
IF #cnt = 2
BREAK
SET #temp+=1
END
SELECT SELECT left(#result ,len(#result)-1)

Related

How to separate a string and insert into table?

My question is that I have a string like this
Red,House|White,Car|Blue,Table
and I want insert this elements in different rows like this
- Col1 Col2
- -----------
- Red House
- White Car
- Blue Table
How can I do it?
maybe this is what are you looking for.
SELECT Substring(value, 1,Charindex(',', value)-1) as col1
, Substring(value, Charindex(',', value)+1, LEN(value)) as col2
FROM STRING_SPLIT('Red,House|White,Car|Blue,Table', '|')
works since SQL Server 2016
You can try this query.
DECLARE #str VARCHAR(500) = 'Red,House|White,Car|Blue,Table'
CREATE TABLE #Temp (tDay VARCHAR(100))
WHILE LEN(#str) > 0
BEGIN
DECLARE #TDay VARCHAR(100)
IF CHARINDEX('|',#str) > 0
SET #TDay = SUBSTRING(#str,0,CHARINDEX('|',#str))
ELSE
BEGIN
SET #TDay = #str
SET #str = ''
END
INSERT INTO #Temp VALUES (#TDay)
SET #str = REPLACE(#str,#TDay + '|' , '')
END
SELECT *
FROM #temp
SELECT tday,
PARSENAME(REPLACE(tday,',','.'),2) 'Col1' ,
PARSENAME(REPLACE(tday,',','.'),1) 'Col2'
FROM #temp
You can check the live demo Here.
I go with using string_split() or a similar string splitter function which you can add to your database. However, I would phrase the final extract logic as:
select left(s.value, v.split - 1),
stuff(s.value, 1, v.split, '')
from string_split('Red,House|White,Car|Blue,Table', '|') s cross apply
(values (charindex(',', s.value))) v(split);

How To Sum Numbers With Commas In SQL Server?

i have string field like (1100,2014,4000) i want to separate comma and sum every number with each other for instance:
1100,2014,4000
1 1 0 0
2 0 1 4
4 0 0 0
result
#first =1+2+4=7
#second= 1+0+0=1
#third=0+1+0=1
#forth=0+4+0=4
This might help to get sum of values like this :
SELECT SUM(CAST(SUBSTRING(X.A, 1, 1) AS INT)) first,
SUM(CAST(SUBSTRING(X.A, 2, 1) AS INT)) second,
SUM(CAST(SUBSTRING(X.A, 3, 1) AS INT)) third,
SUM(CAST(SUBSTRING(X.A, 4, 1) AS INT)) fourth
FROM ( SELECT '1100' A
UNION
SELECT '2014' A
UNION
SELECT '4000' A
) X
Try this,
DECLARE #Table TABLE(Value VARCHAR(20))
INSERT INTO #Table VALUES('1100,2014,4000')
DECLARE #First INT, #Second INT, #Third INT, #Fourth INT
SELECT Split.a.value('.', 'VARCHAR(100)') AS Data
INTO #temp
FROM
(
SELECT CAST ('<M>' + REPLACE(Value, ',', '</M><M>') + '</M>' AS XML) AS Value
FROM #Table
) AS A CROSS APPLY Value.nodes ('/M') AS Split(a);
SELECT #First=SUM(Data/1000)
,#Second=SUM((Data%1000)/100)
,#Third=SUM((Data%100)/10)
,#Fourth=SUM((Data%10))
FROM #temp
SELECT #First, #Second, #Third, #Fourth
DROP TABLE #temp
Following Select statement with multiple SQL CTE expressions can be used with the help of a numbers table function and a SQL split string function for splitting numbers list using "," as seperator
For further splitting each number into its numerals, I preferred to use substring function
with cte as (
select
*
from NumbersList n,
dbo.NumbersTable(1,4,1) as nt
), splitted as (
select
list,
id,
i,
substring(val,i,1) val
from cte
cross apply dbo.Split(list,',') v
)
select
distinct
list,
i,
sum(cast(val as int)) over (partition by list, i) sumOf
from splitted
The output for following entries
insert into NumbersList select '1100,2014,4000'
insert into NumbersList select '1111,2222,3456'
is as follows
This, firstly, uses Jeff Moden's DelimitedSplit8K (as I don't know what version of SQl Server you're on). Secondly, your logic seems a little off. You say that the value for your variable #third is 0+4+0, however, that's the 4th characters of the ints. Also, this assumes that all integers are 4 characters long.
WITH VTE AS(
SELECT *
FROM (VALUES('1100,2014,4000')) v(DSn)) --This is your samnple data
SELECT SUM(CONVERT(int,SUBSTRING(RIGHT('0000' + DS.Item,4),N.I, 1))) AS [Sum]
FROM VTE
CROSS APPLY dbo.DelimitedSplit8K (VTE.DSn,',') DS
CROSS APPLY (VALUES (1),(2),(3),(4)) N(I)
GROUP BY N.I;
You can use this function for all item to row in a table.
you can create this function and try then
select * from dbo.string2table('1243,1234,2343',',')
CREATE FUNCTION [dbo].[string2table]
(
#string VARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(
data VARCHAR(256)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (data)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END

Increasing a number in a string

There are some objects encoded as key:value strings and stored in a table, I'd like to increase sequence number of all objects, which is one field in the object.
For example:
ID Value
--------------------------
504 s:0;d:n;e:test;
506 s:1;d:y;e:branch;
507 s:2;d:y;e:;
I'd like to change them to:
ID Value
--------------------------
504 s:1;d:n;e:test;
506 s:2;d:y;e:branch;
507 s:3;d:y;e:;
Is there a simple way to do this?
Is there a simple way to do this?
No not really.
You can find the positions of s: and d: and then use that to extract the number inbetween, increase it by one and stuff it back into where it belongs.
declare #T table
(
ID int,
Value varchar(50)
);
insert into #T values
(504, 's:0;d:n;e:test;'),
(506, 's:1;d:y;e:branch;'),
(507, 's:2;d:y;e:;');
select T.ID,
stuff(T.Value, P.S, P.D - P.S - 1, S.Value) as NewValue
from #T as T
cross apply (values(charindex('s:', T.Value) + 2,
charindex('d:', T.Value))) as P(S, D)
cross apply (values(substring(T.Value, P.S, P.D - P.S - 1) + 1)) as S(Value)
A version where you find the ; after s: instead of d: as suggested by Eric in a comment.
select T.ID,
stuff(T.Value, S.Pos, SEnd.Pos - S.Pos, V.NewValue) as NewValue
from #T as T
cross apply (values(charindex('s:', T.Value) + 2)) as S(Pos)
cross apply (values(charindex(';', T.Value, S.Pos))) as SEnd(Pos)
cross apply (values(substring(T.Value, S.Pos, SEnd.Pos - S.Pos) + 1)) as V(NewValue)
DECLARE #val nvarchar(200)
SET #val = 's:1;d:y;e:branch;'
SELECT 's:' + CONVERT(nvarchar(100), CONVERT(INT, SUBSTRING(#val, charindex(':', #val) + 1, charindex(';', #val) - charindex(':', #val) -1)) + 1) + SUBSTRING(#val, charindex(':', #val),1000)
You can use what's in the SELECT's query in an UPDATE statement to change the table values
Using the split string functions from here:Split strings the right way – or the next best way
declare #string varchar(max)
set #string='504 s:0;d:n;e:test;'
;with cte as(select * from
[dbo].[SplitStrings_Numbers]
(#string,':'))
select b.item+1 from cte c
cross apply
(select * from [dbo].[SplitStrings_Numbers](c.item,';')) b
where isnumeric(b.item)=1
This accounts for empty or non-integer values; it will ignore them in the event they can't be incremented by one.
-- Build Test Data
IF OBJECT_ID('tempdb..#test') IS NOT NULL DROP TABLE #test
CREATE TABLE #test (ID INT, Value VARCHAR(100))
INSERT #test
VALUES
(504,'s:0;d:n;e:test;'),
(506,'s:1;d:y;e:branch;'),
(507,'s:2;d:y;e:;'),
(508,'s:;d:y;e:;'),
(509,'s:xyz;d:y;e:;');
-- Update S: values
WITH sVals AS
(
SELECT ID, Value, TRY_PARSE(SUBSTRING(Value,CHARINDEX('s:',Value)+2,CHARINDEX(';',Value,CHARINDEX('s:',Value))-(CHARINDEX('s:',Value)+2)) AS INT) AS sVal
FROM #test AS t
)
UPDATE s
SET Value = IIF(sVal IS NOT NULL, STUFF(Value,CHARINDEX('s:',Value)+2,CHARINDEX(';',Value,CHARINDEX('s:',Value))-(CHARINDEX('s:',Value)+2),sVal+1), Value)
FROM sVals AS s
-- Check the results
SELECT *
FROM #test
You can as the below:
DECLARE #val VARCHAR(100) = 's:12;d:n;e:test;'
SELECT REPLACE(#val, ':' + SUBSTRING(#val, 3, PATINDEX('%;d:%', #val) - 3) + ';', ':' + CAST(SUBSTRING(#val, 3, PATINDEX('%;d:%', #val) - 3)+ 1 AS VARCHAR(MAX)) + ';')
Result: s:13;d:n;e:test;

How to use substring conditionally before and after two different symbols in SQL SERVER

I have a table A with ID col. Here is sample data -
ID
NT-QR-1499-1(2015)
NT-XYZ-1503-1
NT-RET-546-1(2014)
I need to select everything after first '-' from left and before '(' from the right. However, some records do not have '(', in which case, the second condition would not apply.
Here is what I need -
QR-1499-1
XYZ-1503-1
RET-546-1
You could get it done in a CASE statement, although I'd definitely take any advice from Aaron;
CREATE TABLE #TestData (ID nvarchar(50))
INSERT INTO #TestData (ID)
VALUES
('NT-QR-1499-1(2015)')
,('NT-XYZ-1503-1')
,('NT-RET-546-1(2014)')
SELECT
ID
,CASE
WHEN CHARINDEX('(',ID) = 0
THEN RIGHT(ID, LEN(ID)-CHARINDEX('-',ID))
ELSE LEFT(RIGHT(ID, LEN(ID)-CHARINDEX('-',ID)),CHARINDEX('(',RIGHT(ID, LEN(ID)-CHARINDEX('-',ID)))-1)
END Result
FROM #TestData
Try this:
SELECT y.i, SUBSTRING(ID, x.i + 1, IIF(y.i = 0, LEN(ID), y.i - x.i - 1))
FROM mytable
CROSS APPLY (SELECT CHARINDEX('-', ID)) AS x(i)
CROSS APPLY (SELECT CHARINDEX('(', ID)) AS y(i)
It looks like your column is not actually a single data element, but multiple data elements that have been concatenated together. A bad idea for database design, which is causing the problem that you're having now.
This should give you what you need, but strongly consider separating the column into the required pieces.
SELECT
SUBSTRING(id, CHARINDEX('-', id) + 1, LEN(id) - CHARINDEX('(', REVERSE(id)) - CHARINDEX('-', id))
FROM
My_Table
DECLARE #str varchar(64);
DECLARE #start int;
DECLARE #length int;
SELECT #str = 'NT-QR-1499-1(2015)';
/*SELECT #str = 'NT-XYZ-1503-1';*/
SELECT #start = CHARINDEX('-', #str) + 1;
SELECT #length = CHARINDEX('(', #str) - #start;
IF (#length > 0)
SELECT SUBSTRING(#str, #start, #length)
ELSE
SELECT SUBSTRING(#str, #start, LEN(#str))
GO
SELECT CASE
WHEN CHARINDEX('(',ID) > 0
THEN
SUBSTRING(ID,CHARINDEX('-',ID)+1,(CHARINDEX('(',ID)-CHARINDEX('-',ID)-1))
ELSE
SUBSTRING(ID,CHARINDEX('-',ID)+1)
END AS New_Column_Name
FROM Table_Name
First it will check whether "(" present or not .
If present then it will fetch the data from next position of "-" to before the position of "(".
otherwise it will fetch the data from next position of "-" to till end.

T-SQL split string based on delimiter

I have some data that I would like to split based on a delimiter that may or may not exist.
Example data:
John/Smith
Jane/Doe
Steve
Bob/Johnson
I am using the following code to split this data into First and Last names:
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
The results I would like:
FirstName---LastName
John--------Smith
Jane--------Doe
Steve-------NULL
Bob---------Johnson
This code works just fine as long as all the rows have the anticipated delimiter, but errors out when a row does not:
"Invalid length parameter passed to the LEFT or SUBSTRING function."
How can I re-write this to work properly?
May be this will help you.
SELECT SUBSTRING(myColumn, 1, CASE CHARINDEX('/', myColumn)
WHEN 0
THEN LEN(myColumn)
ELSE CHARINDEX('/', myColumn) - 1
END) AS FirstName
,SUBSTRING(myColumn, CASE CHARINDEX('/', myColumn)
WHEN 0
THEN LEN(myColumn) + 1
ELSE CHARINDEX('/', myColumn) + 1
END, 1000) AS LastName
FROM MyTable
For those looking for answers for SQL Server 2016+. Use the built-in STRING_SPLIT function
Eg:
DECLARE #tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(#tags, ',')
WHERE RTRIM(value) <> '';
Reference: https://msdn.microsoft.com/en-nz/library/mt684588.aspx
Try filtering out the rows that contain strings with the delimiter and work on those only like:
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
WHERE CHARINDEX('/', myColumn) > 0
Or
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
WHERE myColumn LIKE '%/%'
SELECT CASE
WHEN CHARINDEX('/', myColumn, 0) = 0
THEN myColumn
ELSE LEFT(myColumn, CHARINDEX('/', myColumn, 0)-1)
END AS FirstName
,CASE
WHEN CHARINDEX('/', myColumn, 0) = 0
THEN ''
ELSE RIGHT(myColumn, CHARINDEX('/', REVERSE(myColumn), 0)-1)
END AS LastName
FROM MyTable
ALTER FUNCTION [dbo].[split_string](
#delimited NVARCHAR(MAX),
#delimiter NVARCHAR(100)
) RETURNS #t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
AS
BEGIN
DECLARE #xml XML
SET #xml = N'<t>' + REPLACE(#delimited,#delimiter,'</t><t>') + '</t>'
INSERT INTO #t(val)
SELECT r.value('.','varchar(MAX)') as item
FROM #xml.nodes('/t') as records(r)
RETURN
END
I just wanted to give an alternative way to split a string with multiple delimiters, in case you are using a SQL Server version under 2016.
The general idea is to split out all of the characters in the string, determine the position of the delimiters, then obtain substrings relative to the delimiters. Here is a sample:
-- Sample data
DECLARE #testTable TABLE (
TestString VARCHAR(50)
)
INSERT INTO #testTable VALUES
('Teststring,1,2,3')
,('Test')
DECLARE #delimiter VARCHAR(1) = ','
-- Generate numbers with which we can enumerate
;WITH Numbers AS (
SELECT 1 AS N
UNION ALL
SELECT N + 1
FROM Numbers
WHERE N < 255
),
-- Enumerate letters in the string and select only the delimiters
Letters AS (
SELECT n.N
, SUBSTRING(t.TestString, n.N, 1) AS Letter
, t.TestString
, ROW_NUMBER() OVER ( PARTITION BY t.TestString
ORDER BY n.N
) AS Delimiter_Number
FROM Numbers n
INNER JOIN #testTable t
ON n <= LEN(t.TestString)
WHERE SUBSTRING(t.TestString, n, 1) = #delimiter
UNION
-- Include 0th position to "delimit" the start of the string
SELECT 0
, NULL
, t.TestString
, 0
FROM #testTable t
)
-- Obtain substrings based on delimiter positions
SELECT t.TestString
, ds.Delimiter_Number + 1 AS Position
, SUBSTRING(t.TestString, ds.N + 1, ISNULL(de.N, LEN(t.TestString) + 1) - ds.N - 1) AS Delimited_Substring
FROM #testTable t
LEFT JOIN Letters ds
ON t.TestString = ds.TestString
LEFT JOIN Letters de
ON t.TestString = de.TestString
AND ds.Delimiter_Number + 1 = de.Delimiter_Number
OPTION (MAXRECURSION 0)
The examples above work fine when there is only one delimiter, but it doesn't scale well for multiple delimiters. Note that this will only work for SQL Server 2016 and above.
/*Some Sample Data*/
DECLARE #mytable TABLE ([id] VARCHAR(10), [name] VARCHAR(1000));
INSERT INTO #mytable
VALUES ('1','John/Smith'),('2','Jane/Doe'), ('3','Steve'), ('4','Bob/Johnson')
/*Split based on delimeter*/
SELECT P.id, [1] 'FirstName', [2] 'LastName', [3] 'Col3', [4] 'Col4'
FROM(
SELECT A.id, X1.VALUE, ROW_NUMBER() OVER (PARTITION BY A.id ORDER BY A.id) RN
FROM #mytable A
CROSS APPLY STRING_SPLIT(A.name, '/') X1
) A
PIVOT (MAX(A.[VALUE]) FOR A.RN IN ([1],[2],[3],[4],[5])) P
These all helped me get to this. I am still on 2012 but now have something quick that will allow me to split a string, even if string has varying numbers of delimiters, and grab the nth substring from that string. It's quick too. I know this post is old, but it took me forever to find something so hopefully this will help someone else.
CREATE FUNCTION [dbo].[SplitsByIndex]
(#separator VARCHAR(20) = ' ',
#string VARCHAR(MAX),
#position INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #results TABLE
(id INT IDENTITY(1, 1),
chrs VARCHAR(8000)
);
DECLARE #outResult VARCHAR(8000);
WITH X(N)
AS (SELECT 'Table1'
FROM(VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) T(C)),
Y(N)
AS (SELECT 'Table2'
FROM X A1,
X A2,
X A3,
X A4,
X A5,
X A6,
X A7,
X A8), -- Up to 16^8 = 4 billion
T(N)
AS (SELECT TOP (ISNULL(LEN(#string), 0)) ROW_NUMBER() OVER(
ORDER BY
(
SELECT NULL
)) - 1 N
FROM Y),
Delim(Pos)
AS (SELECT t.N
FROM T
WHERE(SUBSTRING(#string, t.N, LEN(#separator + 'x') - 1) LIKE #separator
OR t.N = 0)),
Separated(value)
AS (SELECT SUBSTRING(#string, d.Pos + LEN(#separator + 'x') - 1, LEAD(d.Pos, 1, 2147483647) OVER(
ORDER BY
(
SELECT NULL
))-d.Pos - LEN(#separator))
FROM Delim d
WHERE #string IS NOT NULL)
INSERT INTO #results(chrs)
SELECT s.value
FROM Separated s
WHERE s.value <> #separator;
SELECT #outResult =
(
SELECT chrs
FROM #results
WHERE id = #position
);
RETURN #outResult;
END;
This can be used like this:
SELECT [dbo].[SplitsByIndex](' ',fieldname,2)
from tablename
I would protect the substring operation by always appending a delimiter to the test strings. This makes the parsing much simpler. Your code may now rely on finding the right pattern, and not need to cope with special cases.
SELECT SUBSTRING(myColumn + '/', 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn + '/', CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
It eliminates edge cases and conditionals and cases.
Always add an extra delimiter at the end, then the challenge case is no problem.