remove substring after certain character if there is no number in it - sql

I would like to remove a sub string after the last dot '.' if it does not contain a number.
Example data:
ID Name
1 example.jpg
2 exampleexample01.01.2014
3 example
4 example1.pdf
5 example13.pdf
6 this. is an. example
7 this.is.a.pdf
Desired result:
ID Name
1 example
2 exampleexample01.01.2014
3 example
4 example1
5 example13
6 this. is an. example
7 this.is.a
My solution which removes every sub string after a dot:
SELECT LEFT([Name], CHARINDEX('.', [Name] + '.') - 1 ) AS Name
FROM Table
Edit:
I updated the example to show that there could be multiple dots in one string.

This seems to do what you want; get the position of the last dot, check if those characters contain a number and if they do return Name. If not, string those characters from the end of the string:
SELECT *,
CASE WHEN RIGHT(V.[Name],CI.LastDot) LIKE '%[0-9]%' THEN V.Name ELSE LEFT(V.[Name], LEN(V.Name) - CI.LastDot) END
FROM (VALUES(1,'example.jpg'),
(2,'exampleexample01.01.2014'),
(3,'example'),
(4,'example1.pdf'),
(5,'example13.pdf'))V(ID,Name)
CROSS APPLY(VALUES(CHARINDEX('.',REVERSE(V.Name))))CI(LastDot);

You can use this query. Basically, you find the string before & after the dot, and apply a simple case when based on your requirement.
WITH before_after AS (
SELECT Name,
LEFT([Name], CHARINDEX('.', [Name] + '.') - 1 ) AS before_dot,
SUBSTRING([Name], CHARINDEX('.', [Name]) + 1, LEN([Name]) - CHARINDEX('.', [Name]) + 1) AS after_dot
FROM table
)
SELECT CASE
WHEN after_dot LIKE '%[0-9]%' THEN before_dot
ELSE Name
END Result
FROM before_after;

SELECT case when ISNUMERIC(RIGHT([Name], CHARINDEX('.', [Name] + '.') - 1 )) =1 then [Name]
else LEFT([Name], CHARINDEX('.', [Name] + '.') - 1 ) end as [Name] FROM Table

try this, but please note that PARSENAME can be used only with less than or equal to 3 dots :
SELECT ID, CASE WHEN PARSENAME(REVERSE([Name]),1) LIKE '%[0-9]%' THEN [Name]
ELSE LEFT([Name], LEN([Name]) - CHARINDEX('.', REVERSE([Name]))) END AS [Name],
FROM [Table]
another way is:
SELECT ID, CASE WHEN RIGHT([Name], CHARINDEX('.', REVERSE([Name]))) LIKE '%.[0-9]%' THEN [Name]
ELSE LEFT([Name], LEN([Name]) - CHARINDEX('.', REVERSE([Name]))) END AS [Name]
FROM [Table]

Related

SQL trying to replace middle characters with *

I am trying to replace SQL results with all the middle values with asterix, *. All results are words. I am using SSMS.
The words that are 4-5 letters, it should only show 1 letter in the beginning, one to the end.
6 letters and more, it it should only show 2 letter in the beginning, 2 letters in the end.
1-3 letters, no replacement.
For example:
(I am now using - instead of * so it does not make the text bold).
"Banana" 6 letters should become ba--na
"False" 5 letters should become F---e
"a" stays the same
"Selin is a vegetable and banana is a fruit" becomes "S---n is a ve-----le and ba--na is a f---t."
What I have done so far, is to make this for emails, after the #. But now I want it to happen with every word of the result.
What I've done:
DECLARE #String VARCHAR(100) = 'sample#gmail.com'
SELECT STUFF(STUFF(#STring,
CHARINDEX('#',#String)+2,
(CHARINDEX('.',#String, CHARINDEX('#',#String))-CHARINDEX('#',#String)-3),
REPLICATE('*',CHARINDEX('.',#String, CHARINDEX('#',#String))-CHARINDEX('#',#String)))
,2
,CHARINDEX('#',#String)-3
,REPLICATE('*',CHARINDEX('#',#String)-3))```
With result s----e#g------l.com
instead of -
And I tried the mask method
Select
--select first character from Email and use replicate
SUBSTRING(Sxolia,1,1) + REPLICATE('*',5)+
--function to put asterisks
SUBSTRING(Sxolia,CHARINDEX('#',Sxolia),len(Sxolia)-CHARINDEX('#',Sxolia)+1)
--at this statement i select this part #gmail,com and to first part to become like this A*****#gmail.com
as Emailmask
From [mytable]
With result
B***** Bana is a fruit
And
declare #str nvarchar(max)
select #str = '123456'
select '****' + substring(#str, 5, len(#str) - 3)
Result: ****56
Not what I am looking for.
How should I look into this?
If I had to deal with this in SQL Server I'd operate on each word as a row, however using string_split is not (currently) an option since it does not guarantee ordering.
The following uses json to split the string as an array and provides a key value for ordering, which allows the words to be aggregated in the correct order:
select t.Sentence,
String_Agg( masked, ' ') within group(order by seq) Masked
from t
cross apply (
select seq, [value] word,
case
when l<=3 then [value]
when l<=5 then Stuff([value],2,l-2,Replicate('*',l-2))
else
Stuff([value],3,l-4,Replicate('*',l-4))
end Masked
from (
select j.[value], 1 + Convert(tinyint,j.[key]) Seq
from OpenJson(Concat('["',replace(t.Sentence,' ', '","'),'"]')) j
)w
cross apply (values(Len([value])))x(l)
)w
group by t.Sentence;
See working demo
Result:
I'm not sure how e-mail fits into all this because you're asking for word masks, so I'm going to assume you actually want this. Use divide and conquer to implement this, so first implement an expression that would do this for simplest cases (e.g. single words). Then if you need it for e-mails, just split the e-mails however you see fit and then apply the same expression.
The expression itself is rather simple:
SELECT *
FROM (VALUES
('banana'),
('selin'),
('vegetable')
) words(word)
CROSS
APPLY (SELECT CASE
WHEN ln BETWEEN 4 AND 5
THEN LEFT(word, 1) + REPLICATE('*', ln-2) + RIGHT(word, 1)
WHEN ln >= 6
THEN LEFT(word, 2) + REPLICATE('*', ln-4) + RIGHT(word, 2)
ELSE word
END as result
FROM (VALUES (LEN(words.word))) x(ln)
) calc
This already provides the expected result. You could define a function out of this, if you have the permissions, and use it like so:
SELECT *
FROM (VALUES
('banana'),
('selin'),
('vegetable')
) words(word)
CROSS
APPLY fnMaskWord(word)
Here's a working demo on dbfiddle, it includes the statement to create the function.
Expanding on a few answers:
select case when len(#String) <= 3 then #String
when len(#String) > 3 AND len(#String) <= 5 then
substring(#String, 1, 2) +
REPLICATE('*', Len(#String) - 2) +
substring(#String, Len(#String) - 1, 2)
when len(#String) >= 6 then
substring(#String, 1, 2) +
REPLICATE('*', Len(#String) - 2) +
substring(#String, Len(#String) - 1, 2)
else 'unrecognized length!'
If the length of the string is less than or equal to 3, return the string.
If the length of the string is more than 3 and less than or equal to 5 then create a substring starting at position 1, then replicate * by the length of the string -2 and finally add another substring -1 from the end of the string.
Similar for if the result is over 6 characters.
Else unrecognized length!
Hope this helps understand what's going on!
Maybe this can help
declare #t table (word varchar(50))
insert into #t values ('banana'), ('selin'), ('vegetable')
select case when len(t.word) < 3 then t.word
else left(t.word, 1) + -- take first char from left
replicate('*', Len(t.word) - 2) + -- fill middle with *
right(t.word, 1) -- take last char from right
end
from #t t
this returns
COLUMN1
b****a
s***l
v*******e
If you want to keep 2 chars left and right when the len > 5 then maybe this
select case when len(t.word) < 3 then t.word
when len(t.word) < 6 then
left(t.word, 1) +
replicate('*', len(t.word) - 2) +
right(t.word, 1)
else left(t.word, 2) +
replicate('*', len(t.word) - 4) +
right(t.word, 2)
end
from #t t
The result
COLUMN1
ba**na
s***l
ve*****le
EDIT: What if there is a whole sentence ?
Well then we first split the sentence in words,
and then concat the individual words back together while putting the ** in them
declare #t table (word varchar(50))
insert into #t values ('banana'), ('selin'), ('vegetable'), ('Banana is a fruit')
select t.word,
-- put the words back togheter into the sentence, and ** them while we are at it
( select string_agg(case when len(value) < 3 then value
when len(value) < 6 then
left(value, 1) +
replicate('*', len(value) - 2) +
right(value, 1)
else left(value, 2) +
replicate('*', len(value) - 4) +
right(value, 2)
end,
' ')
)
from #t t
cross apply string_split(t.word, ' ') s -- split the sentence into words
group by t.word
the result is
word COLUMN1
---- -------
banana ba**na
Banana is a fruit Ba**na is a f***t
selin s***n
vegetable ve*****le

Extract substring from a string in SQL Server

I need to extract a part of substring from string which follows as per below.
YY_12.Yellow
ABC_WSA.Thisone_A
SS_4MON.DHHE_A_A
I need to extract the string as per below
Yellow
Thisone
DHHE
You could use something like this:
declare #tbl table (col nvarchar(100));
insert #tbl values ('YY_12.Yellow'), ('ABC_WSA.Thisone_A'), ('SS_4MON.DHHE_A_A')
select *
, charindex('_', col2, 0)
, left(col2,
case
when charindex('_', col2, 0) - 1 > 0
then charindex('_', col2, 0) - 1
else len(col2)
end) [result]
from (
select col
, substring(col, charindex('.', col, 0) + 1, len(col)) [col2]
from #tbl ) rs
I'm going to leave the full code so as you can hopefully understand what I did.
First identify and remove everything up to the dot "." (in the [col2] column in the nested SELECT)
Then I nest that SELECT so I can apply a new logic much easier on the result column from the first SELECT from which I only keep everything up to the underscore "_"
The final result is stored in the [result] column
Try this:
CREATE TABLE app (info varchar(20))
INSERT INTO app VALUES
('YY_12.Yellow'),
('ABC_WSA.Thisone_A'),
('SS_4MON.DHHE_A_A'),
('testnopoint')
SELECT
CASE
WHEN CHARINDEX('.', info) > 0 THEN
CASE
WHEN CHARINDEX('_', info, CHARINDEX('.', info) + 1) > 0 THEN
SUBSTRING(info, CHARINDEX('.', info) + 1, CHARINDEX('_', info, CHARINDEX('.', info) + 1) - CHARINDEX('.', info) - 1)
ELSE
SUBSTRING(info, CHARINDEX('.', info) + 1, LEN(info))
END
END
FROM app
My query, if . is not present returns NULL, if you want returns all string remove the CASE statement
Go on SqlFiddle
You could also try with parsename() function available from SQL Server 2012
select Name, left(parsename(Name,1),
case when charindex('_', parsename(Name,1)) > 0
then charindex('_', parsename(Name,1))-1
else len(parsename(Name,1))
end) [ExtrectedName] from table
This assumes you have always . in your string to read the name after .
Result :
Name ExtrectedName
YY_12.Yellow Yellow
ABC_WSA.Thisone_A Thisone
SS_4MON.DHHE_A_A DHHE
Try this, used STUFF here
SELECT LEFT(STUFF(col,1,CHARINDEX('.',col),''),
CHARINDEX('_',STUFF(col,1,CHARINDEX('.',col),'')+'_')-1
)
FROM #table
Output:-
Yellow
Thisone
DHHE

How to iterate over a string in one line in SQL

I am writing a query that roughly has this structure:
SELECT Name, <calculated-valued> as Version FROM <tables>
This calculated value needs to work like so: I have a varchar column 'Name' that could contain something like 'ABC' and I want to convert each letter into ASCII, and append them back together to form '65.66.67' in this example. (An empty string should return '0') Is there any way to do this?
My approach wasn't very good, but up to 5 characters I could do the following:
SELECT
CASE WHEN LEN(Name) = 0 THEN '0'
ELSE CAST(ASCII(SUBSTRING(Name, 1, 1)) as varchar(max)) +
CASE WHEN LEN(Name) = 1 THEN ''
ELSE '.' + CAST(ASCII(SUBSTRING(Name, 2, 1)) as varchar(max)) +
CASE WHEN LEN(Name) = 2 THEN ''
ELSE '.' + CAST(ASCII(SUBSTRING(Name, 3, 1)) as varchar(max)) +
CASE WHEN LEN(Name) = 3 THEN ''
ELSE '.' + CAST(ASCII(SUBSTRING(Name, 4, 1)) as varchar(max)) +
CASE WHEN LEN(Name) = 4 THEN ''
ELSE '.' + CAST(ASCII(SUBSTRING(Name, 5, 1)) as varchar(max))
END
END
END
END
END AS MyColumn
FROM <tables>
Is there a better way to do this? Ideally a method that can take any length of string?
Either that or can I cast letters into a hierarchyid datatype? I need to create things like 1/2/a/bc/4// or whatever, but hierarchyid doesn't support that. So instead I'm trying to convert it to 1/2/97/98.99/4/0 so I can convert and maintain the correct order. This column is only used for sorting.
Thanks for any help!
One method is a recursive CTE:
with cte as (
select Name, 1 as lev
cast(ascii(substring(name, 1, 1)) as varchar(max)) as ascii_name
from t
union all
select Name, lev + 1,
ascii_name + '.' + cast(ascii(substring(name, lev + 1, 1)) as varchar(max))
from cte
where len(Name) > lev
)
select Name, ascii_name
from cte;
Another option is with an ad-hoc tally table and a CROSS APPLY
Declare #YourTable table (Name varchar(25))
Insert Into #YourTable values
('ABC'),
('Jack'),
('Jill'),
('')
Select A.Name
,Version = isnull(B.String,'0')
From #YourTable A
Cross Apply (
Select String=Stuff((Select '.' +cast(S as varchar(5))
From (Select Top (len(A.Name))
S=ASCII(substring(A.Name,Row_Number() Over (Order By (Select NULL)),1))
From master..spt_values ) S
For XML Path ('')),1,1,'')
) B
Returns
Name String
ABC 65.66.67
Jack 74.97.99.107
Jill 74.105.108.108
0

How to split two words and number between two number?

My table Data looks like
Sno Componet Subcomponent IRNo
1 1 C1 to C100 001
2 1 C101 to C200 002
3 1 C201 to C300 003
4 1 C301,C400 004
5 1 C401,C500 005
If user enter C50 into textbox then it will get the data from First Row.Mean C50 between C1 to C100(C1,C100)
as same as if user enter C340 , then it will the data from SNO 4.
Means C340 between C301,C400(C301 to C400)
How can I write the query for this in sql server?
This is a terrible design and should be replaced with a better one if possible.
If re-designing is not possible then this answer by Eduard Uta is a good one, but still has one drawback compared to my suggested solution:
It assumes that the Subcomponent will always contain exactly one letter and a number, and that the range specified in the table has the same letter in both sides. a range like AB1 to AC100 might be possible (at least I don't think there's a way to prevent it using pure t-sql).
This is the only reason I present my solution as well. Eduard already got my vote up.
DECLARE #Var varchar(50) = 'C50'
-- also try 'AB150' and 'C332'
;WITH CTE AS (
SELECT Sno, Comp, SubComp,
LEFT(FromValue, PATINDEX('%[0-9]%', FromValue)-1) As FromLetter,
CAST(RIGHT(FromValue, LEN(FromValue) - (PATINDEX('%[0-9]%', FromValue)-1)) as int) As FromNumber,
LEFT(ToValue, PATINDEX('%[0-9]%', ToValue)-1) As ToLetter,
CAST(RIGHT(ToValue, LEN(ToValue) - (PATINDEX('%[0-9]%', ToValue)-1)) as int) As ToNumber
FROM
(
SELECT Sno, Comp, SubComp,
LEFT(SubComp,
CASE WHEN CHARINDEX(' to ', SubComp) > 0 THEN
CHARINDEX(' to ', SubComp)-1
WHEN CHARINDEX(',', SubComp) > 0 THEN
CHARINDEX(',', SubComp)-1
END
) FromValue,
RIGHT(SubComp,
CASE WHEN CHARINDEX(' to ', SubComp) > 0 THEN
LEN(SubComp) - (CHARINDEX(' to ', SubComp) + 3)
WHEN CHARINDEX(',', SubComp) > 0 THEN
CHARINDEX(',', SubComp)-1
END
) ToValue
FROM T
) InnerQuery
)
SELECT Sno, Comp, SubComp
FROM CTE
WHERE LEFT(#Var, PATINDEX('%[0-9]%', #Var)-1) BETWEEN FromLetter AND ToLetter
AND CAST(RIGHT(#Var, LEN(#Var) - (PATINDEX('%[0-9]%', #Var)-1)) as int) BETWEEN FromNumber And ToNumber
sqlfiddle here
No comments about the design. One solution for your question is using a CTE to sanitize the range boundaries and get them to a format that you can work with like so:
DECLARE #inputVal varchar(100) = 'C340'
-- sanitize input:
SELECT #inputVal = RIGHT(#inputVal, (LEN(#inputVal)-1))
;WITH cte (Sno,
SubcomponentStart,
SubcomponentEnd,
IRNo
)
AS
(
SELECT
Sno,
CASE WHEN Subcomponent LIKE '%to%'
THEN REPLACE(SUBSTRING(Subcomponent, 2, CHARINDEX('to', Subcomponent)), 'to','')
ELSE REPLACE(SUBSTRING(Subcomponent, 2,CHARINDEX(',', Subcomponent)), ',','')
END as SubcomponentStart,
CASE WHEN Subcomponent LIKE '%to%'
THEN REPLACE(SUBSTRING(Subcomponent, CHARINDEX('to', Subcomponent)+4, LEN(Subcomponent)), 'to', '')
ELSE REPLACE(SUBSTRING(Subcomponent, CHARINDEX(',', Subcomponent)+3, LEN(Subcomponent)), ',', '')
END as SubcomponentEnd,
IRNo
from test
)
SELECT t.*
FROM test t
INNER JOIN cte c
ON t.Sno = c.Sno
WHERE CAST(#inputVal as int) BETWEEN CAST(c.SubcomponentStart as INT) AND CAST(c.SubcomponentEnd as INT)
SQL Fiddle / tested here: http://sqlfiddle.com/#!6/1b9f0/19
For example you're getting UserEntry in variable #UserEntry, entry value is 'C5'.
-- Start From Here --
set #UserEntry = substring(#UserEntry,2,len(#UserEntry)-1)
select * from <tablename> where convert(int,#UserEntry)>=convert(int,SUBSTRING(Subcomponent,2,charindex('to',Subcomponent,1)-2)) and convert(int,#UserEntry)<=convert(int,(SUBSTRING(Subcomponent,charindex('c',Subcomponent,2)+1,len(Subcomponent)-charindex('c',Subcomponent,3))))

Splitting value of a varchar column into two columns

If I have a column in which strings vary in length but they ALL have a slash \ within,
how can I SELECT to have one column display everything BEFORE the \ and another column displaying everything AFTER the \?
name column1 column2
DB5697\DEV DB5697 DEV
I have seen CHARINDEX and REVERSE on MSDN but haven't been able to put together a soltuion.
How can I best split a varchar/string column value into 2 columns in a result set in TSQL ?
what about using PARSENAME function in a tricky way?
USE tempdb;
GO
CREATE TABLE #names
(
id int NOT NULL PRIMARY KEY CLUSTERED
, name varchar(30) NOT NULL
);
GO
INSERT INTO #names (id, name)
VALUES
(1, 'DB5697\DEV'),
(2, 'DB5800\STG'),
(3, 'DB5900\PRD');
GO
SELECT
name
, PARSENAME(REPLACE(name, '\', '.'), 2) AS [Server]
, PARSENAME(REPLACE(name, '\', '.'), 1) AS [Instance]
FROM
#names;
GO
DROP TABLE #names;
GO
The PARSENAME function accepts 2 parameters and gets the name part of a fully qualified name. The second parameter is the part name enumerator.
Value 2 is for SCHEMA and 1 is for OBJECT.
So, with the REPLACE function the "\" char is replaced by "." in order to have a SCHEMA.OBJECT format of your SERVERNAME\INSTANCE values. Then, PARSENAME behave like having a simple object name in the string.
How about the following (SQL Fiddle):
SELECT m.name,
LEFT(m.name, CHARINDEX('\', m.name) - 1) AS column1,
RIGHT(m.name, LEN(m.name) - CHARINDEX('\', m.name)) AS column2
FROM MyTable m
How to handle strings with no \ in them (SQL Fiddle):
SELECT m.name,
CASE WHEN CHARINDEX('\', m.name) = 0 THEN ''
ELSE LEFT(m.name, CHARINDEX('\', m.name) - 1) END AS column1,
CASE WHEN CHARINDEX('\', m.name) = 0 THEN ''
ELSE RIGHT(m.name, LEN(m.name) - CHARINDEX('\', m.name)) END AS column2
FROM MyTable m;
You can use CHARINDEX to check for the character position of the splitter ('/') and use SUBSTRING to split the string.
However care has to be taken to ensure you handle records without splitters else you would invoke an error.
Also in the case where splitter is unavailable, decision has to be made as to which column the data should be mapped to. Here I am mapping data to FirstName and assigning NULL to LastName
DECLARE #TableBuyer TABLE (ID INT, FullName VARCHAR(100))
INSERT INTO #TableBuyer
SELECT '1','Bryan/Greenberg' UNION ALL
SELECT '2','Channing/Tatum' UNION ALL
SELECT '3','Paul/William' UNION ALL
SELECT '4','EricBana' UNION ALL
SELECT '5','James/Lafferty' UNION ALL
SELECT '6','Wentworth/Miller'
SELECT
CASE
WHEN CHARINDEX('/', FullName) > 0 THEN SUBSTRING(FullName, 1, CHARINDEX('/', FullName) - 1)
ELSE FullName
END AS FirstName
,
CASE
WHEN CHARINDEX('/', FullName) > 0 THEN SUBSTRING(FullName, CHARINDEX('/', FullName) + 1, LEN(FullName))
ELSE NULL
END AS LastName
FROM #TableBuyer;
DECLARE #TableBuyer TABLE (ID INT, FullName VARCHAR(100))
INSERT INTO #TableBuyer
SELECT '1','Bryan/Greenberg' UNION ALL
SELECT '2','Channing/Tatum' UNION ALL
SELECT '3','Paul/William' UNION ALL
SELECT '4','EricBana' UNION ALL
SELECT '5','James/Lafferty' UNION ALL
SELECT '6','Wentworth/Miller'
select left(FullName, len(FullName)-CHARINDEX('/', REVERSE(FullName))) as firstname,
substring(FullName, len(FullName)-CHARINDEX('/', REVERSE(FullName))+ 2, len(FullName)) as lastname
from #TableBuyer
OR
select left(FullName, len(FullName)-CHARINDEX('/', REVERSE(FullName))) as firstname,
RIGHT(FullName, len(FullName)-CHARINDEX('/', FullName)) as lastname
from #TableBuyer
There is no "simple" method. Something like this should work:
select left(col, charindex('\', col) - 1) as column1,
right(col, charindex('\', reverse(col)) - 1) as column2
You might need to double up on the backslash ('\\') to get it to work properly.