I need to remove all brackets and their contents from multiple records but I can't figure out the best way to do this.
I would like to be able to write something like
SELECT dbo.RemoveBracketedText(ColumnName) FROM TableName;
And it would convert a record such as 'Hello (World)' to just 'Hello '
Any help is appreciated.
Thanks!
My first approach would probably have been to write a quick c# app to do it or use SSIS and write a package to take care of it, but if your determined to use tsql... it will likely require a combination of a recursive function and some string manipulation.
This is minimally tested but should be close. (i.e. it worked on the sample text you provide in your question).
CREATE FUNCTION RemoveBracketedText (#sourceString varchar(max))
RETURNS varchar(max)
AS
BEGIN
DECLARE #pStart Int
DECLARE #pEnd Int
DECLARE #pTarget varchar(max)
DECLARE #pResult varchar(max)
SET #pStart = CHARINDEX('(', #sourceString)
SET #pEnd = CHARINDEX(')', #sourceString, #pStart) /** start looking from pos of opening bracket */
IF #pEnd > #pStart AND #pEnd > 0 /** basic error avoidance */
BEGIN
SET #pTarget = SUBSTRING(#sourceString, #pStart, #pEnd - #pStart + 1)
SET #pResult = Replace(#sourceString, #pTarget, '')
/** recursion to get rid of more than one set of brackets per string */
IF CHARINDEX('(', #pResult) > 0 AND CHARINDEX(')', #pResult) > CHARINDEX('(', #pResult)
BEGIN
SET #pResult = dbo.RemoveBracketedText(#pResult)
END
END
ELSE
BEGIN
SET #pResult = #sourceString /** no matching set of brackets found */
END
RETURN #pResult
END
select ltrim(rtrim(
replace(
replace(ColumnName,
substring(ColumnName,
charindex('(',ColumnName),
charindex(')',ColumnName) - charindex('(',ColumnName) + 1),
''),
' ',' ')
)) from TableName;
Related
I have the following query:
DECLARE #value as nvarchar(max)
SET #value = '(company.[department] LIKE ''Development'')';
I would like to extract the word between brackets keep it in a value and then put as input in a replace function like this.
select replace(#value, #department, 'another_string');
You will say probably why I don't do it immediately with the replace function.
The case is that this department string may change dynamically to another string for example country and I would like every time to keep this choice and change it with a value.
You can do this in a query via the base string functions:
SELECT
SUBSTRING(col,
CHARINDEX('[', col) + 1,
CHARINDEX(']', col) - CHARINDEX('[', col) - 1) AS output
FROM yourTable;
Caveats include that you only have one bracketed term, and also that this query form of an answer would be usable in your particular scenario.
Demo
your particular case will idealy works with parsenamefunction:
DECLARE #value as nvarchar(max), #department varchar(100);
SET #value = '(company.[department] LIKE ''Development'')';
SET #department = parsename(replace(replace(#value,'[','.'),']','.'),2)
SELECT replace(#value, #department, 'another_string');
will return:
(company.[another_string] LIKE 'Development')
explanation:
replace brackets with dot '.' and your #value will looks like this:
(company..department. LIKE 'Development')
such pattern is similar with:
Server name.Database name.Schema name.Object name
and you can extract the part of string using parsename function, where: 1 = Object name, 2 = Schema name, 3 = Database name, 4 = Server name
link to function here:
https://learn.microsoft.com/en-us/sql/t-sql/functions/parsename-transact-sql
DEMO
Try like following.
DECLARE #value as nvarchar(max)
SET #value = '(company.[department] LIKE ''Development'')';
declare #department NVARCHAR(100)
SELECT #department = Substring(#value,
Charindex('[', #value) + 1,
Charindex(']', #value) -
Charindex('[', #value) - 1)
select Replace(#value, #department, 'another_string');
Output
(company.[another_string] LIKE 'Development')
I have approximately 30,000 records where I need to split the Description field and so far I can only seem to achieve this in Excel. An example Description would be:
1USBCP 2RJ45C6 1DVI 1DP 3MD 3MLP HANDS
Below is my Excel function:
=TRIM(MID(SUBSTITUTE($G309," ",REPT(" ",LEN($G309))),((COLUMNS($G309:G309)-1)*LEN($G309))+1,LEN($G309)))
This is then dragged across ten Excel columns, and splits the description field at each space.
I have seen many questions asked about splitting a string in SQL but they only seem to cover one space, not multiple spaces.
There is no easy function in SQL server to split strings. At least I don't know it. I use usually some trick that I found somewhere in the Internet some time ago. I modified it to your example.
The trick is that first we try to figure out how many columns do we need. We can do it by checking how many empty strings we have in the string. The easiest way is lenght of string - lenght of string without empty string.
After that for each string we try to find start and end of each word by position. At the end we cut simply string by start and end position and assign to coulmns. The details are in the query. Have fun!
CREATE TABLE test(id int, data varchar(100))
INSERT INTO test VALUES (1,'1USBCP 2RJ45C6 1DVI 1DP 3MD 3MLP HANDS')
INSERT INTO test VALUES (2,'Shorter one')
DECLARE #pivot varchar(8000)
DECLARE #select varchar(8000)
SELECT
#pivot=coalesce(#pivot+',','')+'[col'+cast(number+1 as varchar(10))+']'
FROM
master..spt_values where type='p' and
number<=(SELECT max(len(data)-len(replace(data,',',''))) FROM test)
SELECT
#select='
select p.*
from (
select
id,substring(data, start+2, endPos-Start-2) as token,
''col''+cast(row_number() over(partition by id order by start) as varchar(10)) as n
from (
select
id, data, n as start, charindex('','',data,n+2) endPos
from (select number as n from master..spt_values where type=''p'') num
cross join
(
select
id, '' '' + data +'' '' as data
from
test
) m
where n < len(data)-1
and substring(odata,n+1,1) = '','') as data
) pvt
Pivot ( max(token)for n in ('+#pivot+'))p'
EXEC(#select)
Here you can find example in SQL Fiddle
I didn't notice that you want to get rid of multiple blank spaces.
To do it please create some function that preprare your data :
CREATE FUNCTION dbo.[fnRemoveExtraSpaces] (#Number AS varchar(1000))
Returns Varchar(1000)
As
Begin
Declare #n int -- Length of counter
Declare #old char(1)
Set #n = 1
--Begin Loop of field value
While #n <=Len (#Number)
BEGIN
If Substring(#Number, #n, 1) = ' ' AND #old = ' '
BEGIN
Select #Number = Stuff( #Number , #n , 1 , '' )
END
Else
BEGIN
SET #old = Substring(#Number, #n, 1)
Set #n = #n + 1
END
END
Return #number
END
After that use the new version that removes extra spaces.
DECLARE #pivot varchar(8000)
DECLARE #select varchar(8000)
SELECT
#pivot=coalesce(#pivot+',','')+'[col'+cast(number+1 as varchar(10))+']'
FROM
master..spt_values where type='p' and
number<=(SELECT max(len(dbo.fnRemoveExtraSpaces(data))-len(replace(dbo.fnRemoveExtraSpaces(data),' ',''))) FROM test)
SELECT
#select='
select p.*
from (
select
id,substring(data, start+2, endPos-Start-2) as token,
''col''+cast(row_number() over(partition by id order by start) as varchar(10)) as n
from (
select
id, data, n as start, charindex('' '',data,n+2) endPos
from (select number as n from master..spt_values where type=''p'') num
cross join
(
select
id, '' '' + dbo.fnRemoveExtraSpaces(data) +'' '' as data
from
test
) m
where n < len(data)-1
and substring(data,n+1,1) = '' '') as data
) pvt
Pivot ( max(token)for n in ('+#pivot+'))p'
EXEC(#select)
I am probably not understanding your question, but all that you are doing in that formula, can be done almost exactly the same in SQL. I see someone has already answered but to my mind, how can it be necessary to do all that when you can do this. I might be wrong. But here goes.
declare #test as varchar(100)
set #test='abcd1234567'
select right(#test,2)
, left(#test,2)
, len(#test)
, case when len(#test)%2>0
then left(right(#test,round(len(#test)/2,0)+1),1)
else left(right(#test,round(len(#test)/2,0)+1),2) end
Results
67 ab 11 2
So right, left, length and mid can all be achieved.
If the spaces are the "substring" dividers, then: I dont remember well the actual syntax for do-while inside selects of sql, neither have i actually done that per se, but I don't see why it should not be possible. If it doesn't work then you need a temporary table and if that does not work you need a cursor. The cursor would be an external loop around this one to fetch and process a single string at a time. Or you can do something more clever. I am just a novice.
declare #x varchar(1)
declare #n integer
declare #i integer
declare #str varchar(100) -- this is your description. Fetch it and assign it. if in a cursor just use column-name
set #x = null
set #n = 0
set #i = 0
while n < len(#str)
while NOT #x = " "
begin
set #x = left(right(#str,n),1)
n = n+1
end
--insert into or update #temptable blablabla here.
Use i and n to locate substring and then left(right()) it out. or you can SELECT it, but that is a messy procedure if the number of substrings are long. Continue with:
set i = n
set #str = right(#str, i) -- this includes the " ". left() it out at will.
end
Now, a final comment, there should perhaps be a third loop checking for if you are at the last "substring" because I see now this code will throw error when it gets to the end. or "add" an empty space at the end to #str, that will also work. But my time is up. This is a suggestion at least.
in Sql server
I have a following string
DECLARE #str nvarchar(max);
set #str = "Hello how are you doing today,Its Monday and 5 waiting days";
DECLARE #srch nvarchar(max);
set #srch = " how,doing,monday,waiting";
Now i want to check whether str contains any of string (comma separated string) of srch
I want it in only sql server
is there possibilites to write some query with in clause
like
select from #str where _____ in (select * from CommaSplit(#srch)
where CommaSplit function rerturns rows of #srch comma separted value
I dont want to use cursor or any loop concept as the #srch value can be very long
Thanks
you can use same function to get first string in rows
select string from CommaSplit(#srch,'') where string in (select * from CommaSplit(#srch)
You can use the following common table expressions query to split your string into parts. cte will contain one record per phrase in #srch. In my example below, I show where in #str each of the search phrase is located. It returns 0 if it cannot locate a search phrase.
Note 1: it won't show the location twice if your search phrase is duplicated - you would need another CTE for that.
Note 2: I have to add comma at the end of #srch to make my CTE work. You can do that inside the CTE if you prefer not to change the search string.
DECLARE #str nvarchar(max);
set #str = 'Hello how are you doing today,Its Monday and 5 waiting days';
DECLARE #srch nvarchar(max);
set #srch = 'how,doing,monday,waiting';
set #srch = #srch + ','
-- first split the text into 1 character per row
;with cte
as
(
select substring(#srch, 1, CHARINDEX(',', #srch, 1) - 1) as Phrase, CHARINDEX(',', #srch, 1) as Idx
union all
select substring(#srch, cte.Idx + 1, CHARINDEX(',', #srch, cte.Idx + 1) - cte.Idx - 1) as Phrase, CHARINDEX(',', #srch, cte.Idx + 1) as Idx
from cte
where cte.Idx < CHARINDEX(',', #srch, cte.Idx + 1)
)
select charindex(cte.Phrase, #str, 1) from cte
I don't think that the IN clause is what you need. Instead of this you can use the LIKE construction as following:
if (select count(*) from CommaSplit(#srch) where #str like '%' + val + '%') > 0
select 'true'
else
select 'false'
In this case you will receive 'true' when at least 1 result of CommaSplit function exists in the #str text. But in this case you also will receive a 'true' value when the result of CommaSplit function is a part of the word in the #str string.
If you need more accurate solution, this can be achieved by the following way: you need to split the #str into the words (also replacing punctuation by spaces beforehand). And, after this, intersect of CommaSplit (#srch) and SpaceSplit(#str) will be the answer on the question. Among this, you also will be able to check which words are matching between two strings.
The overhead of this method is to create function SpaceSplit which is copy of CommaSplit but with another separator. Or the function CommaSplit can be modified to receive a separator as parameter.
I am using a function that I found here and else where on the internet to try and strip illegal characters from a field.
Create Function [epacube].[StripSpecs](#myString varchar(500),
#invalidChars varchar(100)) RETURNS varchar(500) AS Begin
While PatIndex(#invalidChars, #myString) > 0
Set #myString = Stuff(#myString, PatIndex(#invalidChars, #myString), 1, '')
Return #myString End
in my table I have set my field value to be: set DATA_NAME = 'Pro$d)uc^t'
If I run this query:
SELECT epacube.StripSpecs (
DATA_NAME
,'%$%') FROM TABLE_DATA
It works and I get a value returned of Prod)uc^t
However, if I add another special character, it no longer works:
SELECT epacube.StripSpecs (
DATA_NAME
,'%$)%') FROM TABLE_DATA
returns my original value Pro$d)uc^t
Does anyone have any suggestion for accomplishing what I need to do?
EDIT
As per the answer below here is the code that worked:
Create Function [epacube].[StripSpecs](#myString varchar(500), #invalidChars varchar(100))
RETURNS varchar(500) AS
Begin
While PatIndex('%[' + #invalidChars + ']%', #myString) > 0
Set #myString = Stuff(#myString, PatIndex('%[' + #invalidChars + ']%', #myString), 1, '')
Return #myString
End
As with LIKE, if you want to specify that one of a set of characters should match, use [] to enclose the set.
SELECT epacube.StripSpecs (
DATA_NAME
,'%[$)]%') FROM TABLE_DATA
Although, personally, given the descriptions of the function and parameters, I'd add the %[ and ]% in StripSpecs, and let the caller just give a list of characters (if you don't want to support any other type of pattern being specified)
Does anyone have in their back pocket a function that can achieve this?
Found this here :-
create function ProperCase(#Text as varchar(8000))
returns varchar(8000)
as
begin
declare #Reset bit;
declare #Ret varchar(8000);
declare #i int;
declare #c char(1);
select #Reset = 1, #i=1, #Ret = '';
while (#i <= len(#Text))
select #c= substring(#Text,#i,1),
#Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c) end,
#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
#i = #i +1
return #Ret
end
Results from this:-
select dbo.propercase('ALL UPPERCASE'); -- All Uppercase
select dbo.propercase('MiXeD CaSe'); -- Mixed Case
select dbo.propercase('lower case'); -- Lower Case
select dbo.propercase('names with apostrophe - mr o''reilly '); -- Names With Apostrophe - Mr O'Reilly
select dbo.propercase('names with hyphen - mary two-barrels '); -- Names With Hyphen - Mary Two-Barrels
I'd do this outside of TSQL, in the calling code tbh.
e.g. if you're using .NET, it's just a case of using TextInfo.ToTitleCase.
That way, you leave your formatting code outside of TSQL (standard "let the caller decide how to use/format the data" approach).
This kind of function is better done on the application side, as it will perform relatively poorly in SQL.
With SQL-Server 2005 and above you could write a CLR function that does that and call it from your SQL. Here is an article on how to do this.
If you really want to do this in T-SQL and without a loop, see Tony Rogerson's article "Turning stuff into "Camel Case" without loops"
I haven't tried it... that's what client code it for :-)
No cursors, no while loops, no (inline) sub-queries
-- ===== IF YOU DON'T HAVE A NUMBERS TABLE =================
--CREATE TABLE Numbers (
-- Num INT NOT NULL PRIMARY KEY CLUSTERED WITH(FILLFACTOR = 100)
--)
--INSERT INTO Numbers
--SELECT TOP(11000)
-- ROW_NUMBER() OVER (ORDER BY (SELECT 1))
--FROM master.sys.all_columns a
-- CROSS JOIN master.sys.all_columns b
DECLARE #text VARCHAR(8000) = 'my text to make title-case';
DECLARE #result VARCHAR(8000);
SET #result = UPPER(LEFT(#text, 1));
SELECT
#result +=
CASE
WHEN SUBSTRING(#text, Num - 1, 1) IN (' ', '-') THEN UPPER(SUBSTRING(#text, Num, 1))
ELSE SUBSTRING(#text, Num, 1)
END
FROM Numbers
WHERE Num > 1 AND Num <= LEN(#text);
PRINT #result;
Will any given row only contain a firstname or a lastname that you wish to convert or will it contain full names separated by spaces? Also, are there any other rules you wish to what characters it should "upper" or lower"?
If you can guarantee that it's only first and last names and you aren't dealing with any specialized capitalization such as after an apostrophe, might this do what you're looking for?
SELECT -- Initial of First Name
UPPER(LEFT(FullName, 1))
-- Rest of First Name
+ SUBSTRING(LOWER(FullName), 2, CHARINDEX(' ', FullName, 0) - 2)
-- Space between names
+ ' '
-- Inital of last name
+ UPPER(SUBSTRING(FullName, CHARINDEX(' ', FullName, 0) + 1, 1))
-- Rest of last name
+ SUBSTRING(LOWER(FullName), CHARINDEX(' ', FullName, 0) + 2, LEN(FullName) - CHARINDEX(' ', FullName, 0) + 2)
FROM Employee