Related
I have data shown below. I'd like to extract the last part after the last underscore.
The data before the underscore can be any length but written with the same syntax i means : letters_letters_letters.
So i wrote this code to extract the part after the last underscore then it's works perfectly but i noticed that there is 2 names written differently like (letters_letters-letters )
with - instead of _ at the end.
NAME= (SELECT SUBSTRING('''+#NAME+''', CHARINDEX(''_'','''+#NAME+''',CHARINDEX(''_'','''+#NAME+''')+1)+1, CHARINDEX(''_'','''+#NAME+''') + CHARINDEX(''_'','''+#NAME+''',CHARINDEX(''_'','''+#NAME+''')+1)) FROM TABLE)
My question is : is there away to check or extract string after any character (without specify if it's underscore or other) ?
Can anyone help please.
Column name is like :
BOB_LOU_K
the thow others columns are like :
BOB_LOU-K
Thanks
Create a function that finds the position of the last character that is not a letter and return the substring from that position to the end of the string. Call that function on your query, something like this SELECT Col1, [dbo].[fn_GetLastString](Col1) AS lastString FROM mytable your function fn_LastString would look somehing like this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION fn_GetLastString(#cSearchedExpression VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #pos INT = LEN(#cSearchedExpression)
DECLARE #currentChar VARCHAR(1)
DECLARE #lastString VARCHAR(MAX)
WHILE #pos>0
BEGIN
SET #currentChar = SUBSTRING(#cSearchedExpression, #pos, 1)
IF NOT (#currentChar LIKE '[a-Z ]' )
BEGIN
SET #lastString = SUBSTRING(#cSearchedExpression, #pos, LEN(#cSearchedExpression) - #pos + 1)
BREAK;
END
SET #pos = #pos - 1
END
RETURN #lastString
END
GO
p.s. notice the space in '[a-Z ]' that will include spaces.
select txt
,right(txt, patindex('%[^a-zA-Z]%', reverse(txt) + ' ') - 1) as last_token
from (values ('BOB_LOU_K'), ('BOB_LOU-K'), ('Yet-aonther-badly-written-question'), ('*followed*by*mediocre*answers'), ('at~best'), ('Quite depressing'), ('isn''t it?'), ('Yep')) t(txt)
txt
last_token
BOB_LOU_K
K
BOB_LOU-K
K
Yet-aonther-badly-written-question
question
*followed*by*mediocre*answers
answers
at~best
best
Quite depressing
depressing
isn't it?
Yep
Yep
Fiddle
A a minimal reproducible example is not provided. So, I am shooting from the hip.
If there are some additional characters to take care of, just add them to the #junkChars variable.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, tokens VARCHAR(100));
INSERT INTO #tbl (tokens) VALUES
('BOB_LOU_K'),
('BOB_LOU-K'),
('Yet-aonther-badly-written-question'),
('*followed*by*mediocre*answers'),
('at/the\best'),
('Quite depressing'),
('isn''t it?'),
('Yep');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = '_'
, #junkChars VARCHAR(10) = '-*\ ';;
SELECT t.*
, result = c.value('(/root/r[last()]/text())[1]', 'VARCHAR(30)')
FROM #tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(TRANSLATE(tokens, #junkChars, REPLICATE(#separator, DATALENGTH(#junkChars))), #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t1(c);
Output
ID
tokens
result
1
BOB_LOU_K
K
2
BOB_LOU-K
K
3
Yet-aonther-badly-written-question
question
4
followedbymediocreanswers
answers
5
at/the\best
best
6
Quite depressing
depressing
7
isn't it?
it?
8
Yep
Yep
i have transaction codes like
"A0004", "1B2005","20CCCCCCC21"
I need to extract the rightmost number and increment the transaction code by one
"AA0004"----->"AA0005"
"1B2005"------->"1B2006"
"20CCCCCCCC21"------>"20CCCCCCCC22"
in SQL Server 2012.
unknown length of string
right(n?) always number
dealing with unsignificant number of string and number length is out of my league.
some logic is always missing.
LEFT(#a,2)+RIGHT('000'+CONVERT(NVARCHAR,CONVERT(INT,SUBSTRING( SUBSTRING(#a,2,4),2,3))+1)),3
First, I want to be clear about this: I totally agree with the comments to the question from a_horse_with_no_name and Jeroen Mostert.
You should be storing one data point per column, period.
Having said that, I do realize that a lot of times the database structure can't be changed - so here's one possible way to get that calculation for you.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
col varchar(100)
);
INSERT INTO #T (col) VALUES
('A0004'),
('1B2005'),
('1B2000'),
('1B00'),
('20CCCCCCC21');
(I've added a couple of strings as edge cases you didn't mention in the question)
Then, using a couple of cross apply to minimize code repetition, I came up with that:
SELECT col,
LEFT(col, LEN(col) - LastCharIndex + 1) +
REPLICATE('0', LEN(NumberString) - LEN(CAST(NumberString as int))) +
CAST((CAST(NumberString as int) + 1) as varchar(100)) As Result
FROM #T
CROSS APPLY
(
SELECT PATINDEX('%[^0-9]%', Reverse(col)) As LastCharIndex
) As Idx
CROSS APPLY
(
SELECT RIGHT(col, LastCharIndex - 1) As NumberString
) As NS
Results:
col Result
A0004 A0005
1B2005 1B2006
1B2000 1B2001
1B00 1B01
20CCCCCCC21 20CCCCCCC22
The LastCharIndex represents the index of the last non-digit char in the string.
The NumberString represents the number to increment, as a string (to preserve the leading zeroes if they exists).
From there, it's simply taking the left part of the string (that is, up until the number), and concatenate it to a newly calculated number string, using Replicate to pad the result of addition with the exact number of leading zeroes the original number string had.
Try This
DECLARE #test nvarchar(1000) ='"A0004", "1B2005","20CCCCCCC21"'
DECLARE #Temp AS TABLE (ID INT IDENTITY,Data nvarchar(1000))
INSERT INTO #Temp
SELECT #test
;WITH CTE
AS
(
SELECT Id,LTRIM(RTRIM((REPLACE(Split.a.value('.' ,' nvarchar(max)'),'"','')))) AS Data
,RIGHT(LTRIM(RTRIM((REPLACE(Split.a.value('.' ,' nvarchar(max)'),'"','')))),1)+1 AS ReqData
FROM
(
SELECT ID,
CAST ('<S>'+REPLACE(Data,',','</S><S>')+'</S>' AS XML) AS Data
FROM #Temp
) AS A
CROSS APPLY Data.nodes ('S') AS Split(a)
)
SELECT CONCAT('"'+Data+'"','-------->','"'+CONCAT(LEFT(Data,LEN(Data)-1),CAST(ReqData AS VARCHAR))+'"') AS ExpectedResult
FROM CTE
Result
ExpectedResult
-----------------
"A0004"-------->"A0005"
"1B2005"-------->"1B2006"
"20CCCCCCC21"-------->"20CCCCCCC22"
STUFF(#X
,LEN(#X)-CASE PATINDEX('%[A-Z]%',REVERSE(#X)) WHEN 0 THEN LEN(#X) ELSE PATINDEX('%[A-Z]%',REVERSE(#X))-1 END+1
,LEN(((RIGHT(#X,CASE PATINDEX('%[A-Z]%',REVERSE(#X)) WHEN 0 THEN LEN(#X) ELSE PATINDEX('%[A-Z]%',REVERSE(#X))-1 END)/#N)+1)#N)
,((RIGHT(#X,CASE PATINDEX('%[A-Z]%',REVERSE(#X)) WHEN 0 THEN LEN(#X) ELSE PATINDEX('%[A-Z]%',REVERSE(#X))-1 END)/#N)+1)#N)
works on number only strings
99 becomes 100
mod(#N) increments
I would like to remove all characters before and after a string in the select statement.
In the example below I would like to remove everything before and including /Supply> and after and including >/
Note the remaining part will be a fixed number of characters.
Any help would be much appreciated
Eg.
abs/Supply>hhfhjgglldppprrr>/llllllldsfsjhfhhhfdhudfhfhdhdfhfsd
Would become:
hhfhjgglldppprrr
If your input always has exactly two instances of ">" you could use PARSENAME.
declare #SomeValue varchar(100) = 'abs/Supply>hhfhjgglldppprrr>/llllllldsfsjhfhhhfdhudfhfhdhdfhfsd'
select PARSENAME(replace(#SomeValue, '>', '.'), 2)
This will not work correctly if your data also contains any periods (.). We can deal with that if needed with a couple of replace statements. Still very simple and easy to maintain with the same caveat of exactly 2 >.
declare #SomeOtherValue varchar(100) = 'abs/Supply>hhfhjgg.lldppprrr>/llllllldsfsjhfhhhfdhudfhfhdhdfhfsd'
select replace(PARSENAME(replace(replace(#SomeOtherValue, '.', '~!##'), '>', '.'), 2), '~!##', '.')
You can use PATINDEX() to identify the position of the patterns you are looking for (/Supply> and >/) then remove them based on the length of the string:
SELECT LEFT(RIGHT(col,LEN(col) - PATINDEX('%/Supply>%',col) -7), PATINDEX('%>/%', RIGHT(col,LEN(col) - PATINDEX('%Supply>%',col) -7))-1)
Simply replace col in the above with your column name.
Example below with test string abs/Supply>keep>/remove
First remove everything before and including /Supply>:
SELECT RIGHT('abs/Supply>keep>/remove',LEN('abs/Supply>keep>/remove') - PATINDEX('%/Supply>%','abs/Supply>keep>/remove') -7)
This will give keep>/remove
Then remove everything after and including >/:
SELECT LEFT('keep>/remove',PATINDEX('%>/%','keep>/remove') - 1)
This will give keep, the part of the string you want.
Here is the combined version, same as above, just includes the test string instead of col so you can run it easily:
SELECT LEFT(RIGHT('abs/Supply>keep>/remove',LEN('abs/Supply>keep>/remove') - PATINDEX('%/Supply>%','abs/Supply>keep>/remove') -7), PATINDEX('%>/%', RIGHT('abs/Supply>keep>/remove',LEN('abs/Supply>keep>/remove') - PATINDEX('%/Supply>%','abs/Supply>keep>/remove') -7))-1)
This will give keep. You can also replace the string above with the one in your question, I just used a different test string because it is shorter and makes the code more readable.
try this:
DECLARE #inputStr VARCHAR(max)= 'abs/Supply>hhfhjgglldppprrr>/llllllldsfsjhfhhhfdhudfhfhdhdfhfsd'
DECLARE #startString VARCHAR(100)='/Supply>'
DECLARE #EndString VARCHAR(100)='>/'
DECLARE #LenStartString INT = LEN(#startString)
DECLARe #TempInputString VARCHAR(max)='';
DECLARE #StartIndex INT
DECLARE #EndIndex INT
SELECT #StartIndex = CHARINDEX(#startString,#inputStr)+#LenStartString
SELECT #TempInputString = STUFF(#inputStr, 1, #StartIndex, '')
SELECT SUBSTRING(#TempInputString,0,CHARINDEX(#EndString,#TempInputString))
In Single Line
DECLARE #inputStr VARCHAR(max)= 'abs/Supply>hhfhjgglldppprrr>/llllllldsfsjhfhhhfdhudfhfhdhdfhfsd'
DECLARE #startString VARCHAR(100)='/Supply>'
DECLARE #EndString VARCHAR(100)='>/'
SELECT SUBSTRING(STUFF(#inputStr, 1, CHARINDEX(#startString,#inputStr)+LEN(#startString), ''),0,CHARINDEX(#EndString,STUFF(#inputStr, 1,CHARINDEX(#startString,#inputStr)+LEN(#startString), '')))
Hi I want to cut a string in sql at 2 points with the "/" as an indicator where to cut. The string looks like this: "test1/test2/test3/test4/test5/test6" and I need the parts of test 2 and test 5 but they are not static, so I have to use the "/" befor and after them to set the points where to cut, any suggestions?
You could achieve this by using XML:
DECLARE #x NVARCHAR(100) = 'test1/test2/test3/test4/test5/test6';
DECLARE #xml XML = cast(('<X>'+replace(#x,'/' ,'</X><X>')+'</X>') as xml);
WITH cte AS(
SELECT N.value('.', 'varchar(10)') as value, ROW_NUMBER() OVER (ORDER BY (SELECT(0))) AS rn
FROM #xml.nodes('X') as T(N)
)
SELECT *
FROM cte
WHERE rn IN (1, 5)
You can use the function CHARINDEX()
For example this query will work for you
declare #text varchar(100)='test1/test2/test3/test4/test5/test6'
select
SUBSTRING (#text,CHARINDEX('/',#text)+1,CHARINDEX('/',#text,(CHARINDEX('/',#text)+1))-CHARINDEX('/',#text)-1),
SUBSTRING (#text,CHARINDEX('/',#text,CHARINDEX('/',#text,CHARINDEX('/',#text,CHARINDEX('/',#text)+1)+1)+1)+1,CHARINDEX('/',#text,(CHARINDEX('/',#text)+1))-CHARINDEX('/',#text)-1)
Just incase you want to substring something else, I'm sharing the code below and then you can work on it
declare #text varchar(100)='test1/test2/test3/test4/test5/test6'
select
SUBSTRING (#text,CHARINDEX('/',#text)+1,CHARINDEX('/',#text,(CHARINDEX('/',#text)+1))-CHARINDEX('/',#text)-1) --test2
,SUBSTRING (#text,CHARINDEX('/',#text,CHARINDEX('/',#text)+1)+1,CHARINDEX('/',#text,(CHARINDEX('/',#text)+1))-CHARINDEX('/',#text)-1) --test3
,SUBSTRING (#text,CHARINDEX('/',#text,CHARINDEX('/',#text,CHARINDEX('/',#text)+1)+1)+1,CHARINDEX('/',#text,(CHARINDEX('/',#text)+1))-CHARINDEX('/',#text)-1) --test4
,SUBSTRING (#text,CHARINDEX('/',#text,CHARINDEX('/',#text,CHARINDEX('/',#text,CHARINDEX('/',#text)+1)+1)+1)+1,CHARINDEX('/',#text,(CHARINDEX('/',#text)+1))-CHARINDEX('/',#text)-1) --test5
If the items that you want are always the second and the second to last you can use CHARINDEX to find the first and second '/' and SUBSTRING to pull that value. Then REVERSE the string to get the second last. e.g.
DECLARE #data varchar(128) = 'test1/test2/test3/test4/test5/test6';
SELECT substring(#data,charindex('/',#data,1)+1,charindex('/',#data,charindex('/',#data,1)+1)-(charindex('/',#data,1)+1)),
reverse(substring(reverse(#data),charindex('/',reverse(#data),1)+1,charindex('/',reverse(#data),charindex('/',reverse(#data),1)+1)-(charindex('/',reverse(#data),1)+1)))
There's already an answer for this question in SO with a MySQL tag. So I just decided to make your lives easier and put the answer below for SQL Server users. Always happy to see different answers perhaps with a better performance.
Happy coding!
SELECT SUBSTRING(#YourString, 1, LEN(#YourString) - CHARINDEX(' ', REVERSE(#YourString)))
Edit: Make sure #YourString is trimmed first as Alex M has pointed out:
SET #YourString = LTRIM(RTRIM(#YourString))
Just an addition to answers.
The doc for LEN function in MSSQL:
LEN excludes trailing blanks. If that is a problem, consider using the DATALENGTH (Transact-SQL) function which does not trim the string. If processing a unicode string, DATALENGTH will return twice the number of characters.
The problem with the answers here is that trailing spaces are not accounted for.
SELECT SUBSTRING(#YourString, 1, LEN(#YourString) - CHARINDEX(' ', REVERSE(#YourString)))
As an example few inputs for the accepted answer (above for reference), which would have wrong results:
INPUT -> RESULT
'abcd ' -> 'abc' --last symbol removed
'abcd 123 ' -> 'abcd 12' --only removed only last character
To account for the above cases one would need to trim the string (would return the last word out of 2 or more words in the phrase):
SELECT SUBSTRING(RTRIM(#YourString), 1, LEN(#YourString) - CHARINDEX(' ', REVERSE(RTRIM(LTRIM(#YourString)))))
The reverse is trimmed on both sides, that is to account for the leading as well as trailing spaces.
Or alternatively, just trim the input itself.
DECLARE #Sentence VARCHAR(MAX) = 'Hi This is Pavan Kumar'
SELECT SUBSTRING(#Sentence, 1, CHARINDEX(' ', #Sentence) - 1) AS [First Word],
REVERSE(SUBSTRING(REVERSE(#Sentence), 1,
CHARINDEX(' ', REVERSE(#Sentence)) - 1)) AS [Last Word]
DECLARE #String VARCHAR(MAX) = 'One two three four'
SELECT LEFT(#String,LEN(#String)-CHARINDEX(' ', REVERSE(#String),0)+1)
All the answers so far are actually about removing a character, not a word as the OP wanted.
In my case I was building a dynamic SQL statement with UNION'd SELECT statements and wanted to remove the last UNION:
DECLARE #sql NVARCHAR(MAX) = ''
/* populate #sql with something like this:
SELECT 1 FROM dbo.T1 WHERE condition
UNION
SELECT 1 FROM dbo.T2 WHERE condition
UNION
SELECT 1 FROM dbo.T3 WHERE condition
UNION
SELECT 1 FROM dbo.T4 WHERE condition
UNION
*/
-- remove the last UNION
SET #sql = SUBSTRING(#sql, 1, LEN(#sql) - PATINDEX(REVERSE('%UNION%'), REVERSE(#sql)) - LEN('UNION'))
SELECT LEFT(username , LEN(json_path) - CHARINDEX('/', REVERSE(username ))+1)
FROM Login_tbl
UPDATE Login_tbl
SET username = LEFT(username , LEN(json_path) - CHARINDEX('/', REVERSE(username ))+1)
DECLARE #String VARCHAR(MAX) = 'One two three four'
SELECT LEFT(#String,LEN(#String)-CHARINDEX(' ', REVERSE(#String),0)+1)