SQL: How to find the count of words in following example?
declare #s varchar(55) = 'How to find the count of words in this string ?'
Subquestions:
How to count spaces?
How to count double/triple/... spaces as one? answer by Gordon Linoff here
How to avoid counting of special characters? Example: 'Please , don't count this comma'
Is it possible without string_split function (because it's available only since SQL SERVER 2016)?
Summary with the best solutions HERE
Thanks to Gordon Linoff's answer here
SELECT len(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','))
OutPut
-------
How,to,find,the,count,of,words,in,this,string?
SELECT replace(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','),',','')
OutPut
------
Howtofindthecountofwordsinthisstring?
Now you can find the difference between the length of both the output and add 1 for the last word like below.
declare #s varchar(55) = 'How to find the count of words in this string?'
SELECT len(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','))
-len(replace(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','),',',''))
+ 1 AS WORD_COUNT
WORD_COUNT
----------
10
http://sqlfiddle.com/#!18/06c1d/5
One method uses a recursive CTE:
declare #s varchar(55) = 'How to find the count of words in this string ?';
with cte as (
select convert(varchar(max), '') as word,
convert(varchar(max), ltrim(#s)) as rest
union all
select left(rest, patindex('%[ ]%', rest + ' ') - 1),
ltrim(stuff(rest, 1, patindex('%[ ]%', rest + ' '), ''))
from cte
where rest <> ''
)
select count(*)
from cte
where word not in ('', '?', ',')
--OPTION (MAXRECURSION 1000); -- use if number of words >99
;
Here is a db<>fiddle.
First thing is you need to remove the double/tripple.. or more count into one.
declare #str varchar(500) = 'dvdv sdd dfxdfd dfd'
select Replace(Replace(Replace( #str,' ',']['), '[]', ''), '][', ' ')
this will remove all the unnecessary space in between the word and you'll get your final word.
After that you may use string_split (for SQL SERVER 2016 and above). To count the number of word in your text from which minus 1 is your total count of spaces.
select count(value) - 1 from string_split( #str, ' ')
Final query looks like
declare #str varchar(500) = 'dvdv sdd dfxdfd dfd'
select count(value) - 1 from string_split( Replace(Replace(Replace( #str,' ',']['), '[]', ''), '][', ' '), ' ')
For only word count and if your MSSQL Version support STRING_SPLIT, you can use this simple script below-
DECLARE #s VARCHAR(55) = 'How to find the count of words in this string ?'
SELECT
COUNT(
IIF(
LTRIM(value)='',
NULL,
1
)
)
FROM STRING_SPLIT(#s, ' ')
WHERE value LIKE '%[0-9,A-z]%'
Using string_split (available only since SQL SERVER 2016):
declare #string varchar(55) = 'How to find the count of words in this string ?';
select count(*) WordCount from string_split(#string,' ') where value like '%[0-9A-Za-z]%'
The same idea is used in following answers:
https://stackoverflow.com/a/57783421/6165594
https://stackoverflow.com/a/57783743/6165594
Without using string_split:
declare #string varchar(55) = 'How to find the count of words in this string ?';
;with space as
( -- returns space positions in a string
select cast( 0 as int) idx union all
select cast(charindex(' ', #string, idx+1) as int) from space
where charindex(' ', #string, idx+1)>0
)
select count(*) WordCount from space
where substring(#string,idx+1,charindex(' ',#string+' ',idx+1)-idx-1) like '%[0-9A-Za-z]%'
OPTION (MAXRECURSION 0);
The same idea is used in following answers:
https://stackoverflow.com/a/57787850/6165594
As Inline Function:
ALTER FUNCTION dbo.WordCount
(
#string NVARCHAR(MAX)
, #WordPattern NVARCHAR(MAX) = '%[0-9A-Za-z]%'
)
/*
Call Example:
1) Word count for single string:
select * from WordCount(N'How to find the count of words in this string ? ', default)
2) Word count for set of strings:
select *
from (
select 'How to find the count of words in this string ? ' as string union all
select 'How many words in 2nd example?'
) x
cross apply WordCount(x.string, default)
Limitations:
If string contains >100 spaces function fails with error:
Msg 530, Level 16, State 1, Line 45
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
NB! OPTION (MAXRECURSION 0); -- don't work within inline function
*/
RETURNS TABLE AS RETURN
(
with space as
( -- returns space positions in a string
select cast( 0 as int) idx union all
select cast(charindex(' ', #string, idx+1) as int) from space
where charindex(' ', #string, idx+1)>0
)
select count(*) WordCount from space
where substring(#string,idx+1,charindex(' ',#string+' ',idx+1)-idx-1) like #WordPattern
-- OPTION (MAXRECURSION 0); -- don't work within inline function
);
go
Related
I have the following string:
'Siemens','Simatic','Microbox','PC','27','6ES7677AA200PA0','6ES7','677AA200PA0'
I want to remove any "terms" that are less than 5 characters. So in this case I'd like to remove 'PC', '27' and '6ES7'.
Which would result in:
'Siemens','Simatic','Microbox','6ES7677AA200PA0','677AA200PA0'
This is in SQL server and I have a function that accepts a regex command, so far it looks like this:
SELECT dbo.fn_StripCharacters(title, '/^''PC''$/')
I tried to hardcode to remove 'PC' but I think its removing all apostrophes, and 'P' and 'C' characters:
Siemens,Simati,Mirobox,,427B,6ES76477AA200A0,6ES7,6477AA200A0
This is the function I'm using:
CREATE FUNCTION [dbo].[fn_StripCharacters]
(
#String NVARCHAR(MAX),
#MatchExpression VARCHAR(255)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
SET #MatchExpression = '%['+#MatchExpression+']%'
WHILE PatIndex(#MatchExpression, #String) > 0
SET #String = Stuff(#String, PatIndex(#MatchExpression, #String), 1, '')
RETURN #String
END
If you don't care about the particular order of the words which are retained after filtering off words 4 characters or less, you could use STRING_SPLIT and STRING_AGG:
WITH cte AS (
SELECT id, value
FROM yourTable
CROSS APPLY STRING_SPLIT(val, ',')
)
SELECT id, STRING_AGG(value, ',') AS val
FROM cte
WHERE LEN(value) > 6
GROUP BY id;
Demo
This is my String
Declare #qstr as varchar(max)='hireteammember.aspx?empemail=kuldeep#asselsolutions.com&empid=376&empname=kuldeep&adminname=TMA1&term=5&teamid=161&contactid=614¥1&WP=100¥5¥Months&Amt=500&DueDay=5&StrDt=12/31/2013&MemCatg=Employees&StrTm=21:05&PlnHrs=5&WrkDays=true¥true¥true¥true¥true¥false¥false'
I want to extract the values of empid,empname,adminname,term,teamid,contactid,WP,Months,Dueday,StrDt,MemCatgmStrTm,PlnHrs,WrkDays and assign them to new variables
I have used
select ( SUBSTRING(#qstr,CHARINDEX('=',#qstr)+1,CHARINDEX('&',#qstr)-CHARINDEX('=',#qstr)-1)))
but only getting the 'empemail' , for the next occurance of special char '&' , not able to get the values of further terms , if i am using '&' in spite of '=' .
Help me to split the whole string
How about using XML to split the values into rows, and then splitting them into columns.
Something like
Declare #qstr as varchar(max)='hireteammember.aspx?empemail=kuldeep#asselsolutions.com&empid=376&empname=kuldeep&adminname=TMA1&term=5&teamid=161&contactid=614¥1&WP=100¥5¥Months&Amt=500&DueDay=5&StrDt=12/31/2013&MemCatg=Employees&StrTm=21:05&PlnHrs=5&WrkDays=true¥true¥true¥true¥true¥false¥false'
DECLARe #str VARCHAR(MAX) = SUBSTRING(#qstr,CHARINDEX('?',#qstr,0) + 1, LEN(#qstr)-CHARINDEX('?',#qstr,0))
DECLARE #xml XML
SELECT #xml = CAST('<d>' + REPLACE(#str, '&', '</d><d>') + '</d>' AS XML)
;WITH Vals AS (
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM #xml.nodes('/d') T(split)
)
SELECT LEFT(data,CHARINDEX('=',data,0) - 1),
RIGHT(data,LEN(data) - CHARINDEX('=',data,0))
FROM Vals
SQL Fiddle DEMO
CREATE FUNCTION dbo.SplitQueryString (#s varchar(8000))
RETURNS table
AS
RETURN (
WITH splitter_cte AS (
SELECT CHARINDEX('&', #s) as pos, 0 as lastPos
UNION ALL
SELECT CHARINDEX('&', #s, pos + 1), pos
FROM splitter_cte
WHERE pos > 0
),
pair_cte AS (
SELECT chunk,
CHARINDEX('=', chunk) as pos
FROM (
SELECT SUBSTRING(#s, lastPos + 1,
case when pos = 0 then 80000
else pos - lastPos -1 end) as chunk
FROM splitter_cte) as t1
)
SELECT substring(chunk, 0, pos) as keyName,
substring(chunk, pos+1, 8000) as keyValue
FROM pair_cte
)
GO
declare #queryString varchar(2048)
set #queryString = 'foo=bar&temp=baz&key=value';
SELECT *
FROM dbo.SplitQueryString(#queryString)
OPTION(MAXRECURSION 0);
when run produces the following output.
keyName keyValue
------- --------
foo bar
temp baz
key value
(3 row(s) affected)
I believe that this will do exactly what you are asking.
SQL FIDDLE DEMO
If the order of the values in the html string remains same i would suggest using the whole string name like
select ( SUBSTRING(#qstr,CHARINDEX('empemail=',#qstr)+1,CHARINDEX('&empid=',#qstr)-CHARINDEX('empemail=',#qstr)-1)))
If you are still looking for nth occurance then refer to this link
Declare #qstr as varchar(max)='hireteammember.aspx?empemail=kuldeep#asselsolutions.com&empid=376&empname=kuldeep&adminname=TMA1&term=5&teamid=161&contactid=614¥1&WP=100¥5¥Months&Amt=500&DueDay=5&StrDt=12/31/2013&MemCatg=Employees&StrTm=21:05&PlnHrs=5&WrkDays=true¥true¥true¥true¥true¥false¥false'
(select ( SUBSTRING(#qstr,CHARINDEX('&empname=',#qstr)+1,CHARINDEX('&adminname=',#qstr)-CHARINDEX('&empname=',#qstr)-1)))
(select ( SUBSTRING(#qstr,CHARINDEX('?empemail=',#qstr)+1,CHARINDEX('&empid=',#qstr)-CHARINDEX('?empemail=',#qstr)-1)))
like this i have splitted and updated The whole string. Thank you All for your answers, Your answers Helped me to solve this
How do I get the nth word in a sentence or a set of strings with space delimiter?
Sorry for the change in the requirement.Thank you.
By using instr.
select substr(help, 1, instr(help,' ') - 1)
from ( select 'hello my name is...' as help
from dual )
instr(help,' ') returns the positional index of the first occurrence of the second argument in the first, inclusive of the string you're searching for. i.e. the first occurrence of ' ' in the string 'hello my name is...' plus the space.
substr(help, 1, instr(help,' ') - 1) then takes the input string from the first character to the index indicated in instr(.... I then remove one so that the space isn't included..
For the nth occurrence just change this slightly:
instr(help,' ',1,n) is the nth occurrence of ' ' from the first character. You then need to find the positional index of the next index instr(help,' ',1,n + 1), lastly work out the difference between them so you know how far to go in your substr(.... As you're looking for the nth, when n is 1 this breaks down and you have to deal with it, like so:
select substr( help
, decode( n
, 1, 1
, instr(help, ' ', 1, n - 1) + 1
)
, decode( &1
, 1, instr(help, ' ', 1, n ) - 1
, instr(help, ' ', 1, n) - instr(help, ' ', 1, n - 1) - 1
)
)
from ( select 'hello my name is...' as help
from dual )
This will also break down at n. As you can see this is getting ridiculous so you might want to consider using regular expressions
select regexp_substr(help, '[^[:space:]]+', 1, n )
from ( select 'hello my name is...' as help
from dual )
Try this. An example of getting the 4th word:
select names from (
select
regexp_substr('I want my two dollars','[^ ]+', 1, level) as names,
rownum as nth
from dual
connect by regexp_substr('I want my two dollars', '[^ ]+', 1, level) is not null
)
where nth = 4;
The inner query is converting the space-delimited string into a set of rows. The outer query is grabbing the nth item from the set.
Try something like
WITH q AS (SELECT 'ABCD EFGH IJKL' AS A_STRING FROM DUAL)
SELECT SUBSTR(A_STRING, 1, INSTR(A_STRING, ' ')-1)
FROM q
Share and enjoy.
And here's the solution for the revised question:
WITH q AS (SELECT 'ABCD EFGH IJKL' AS A_STRING, 3 AS OCCURRENCE FROM DUAL)
SELECT SUBSTR(A_STRING,
CASE
WHEN OCCURRENCE=1 THEN 1
ELSE INSTR(A_STRING, ' ', 1, OCCURRENCE-1)+1
END,
CASE
WHEN INSTR(A_STRING, ' ', 1, OCCURRENCE) = 0 THEN LENGTH(A_STRING)
ELSE INSTR(A_STRING, ' ', 1, OCCURRENCE) - CASE
WHEN OCCURRENCE=1 THEN 0
ELSE INSTR(A_STRING, ' ', 1, OCCURRENCE-1)
END - 1
END)
FROM q;
Share and enjoy.
CREATE PROC spGetCharactersInAStrings
(
#S VARCHAR(100) = '^1402 WSN NI^AMLAB^tev^e^^rtS htimS 0055518',
#Char VARCHAR(100) = '8'
)
AS
-- exec spGetCharactersInAStrings '^1402 WSN NI^AMLAB^tev^e^^rtS htimS 0055518', '5'
BEGIN
DECLARE #i INT = 1,
#c INT,
#pos INT = 0,
#NewStr VARCHAR(100),
#sql NVARCHAR(100),
#ParmDefinition nvarchar(500) = N'#retvalOUT int OUTPUT'
DECLARE #D TABLE
(
ID INT IDENTITY(1, 1),
String VARCHAR(100),
Position INT
)
SELECT #c = LEN(#S), #NewStr = #S
WHILE #i <= #c
BEGIN
SET #sql = ''
SET #sql = ' SELECT #retvalOUT = CHARINDEX(''' + + #Char + ''',''' + #NewStr + ''')'
EXEC sp_executesql #sql, #ParmDefinition, #retvalOUT=#i OUTPUT;
IF #i > 0
BEGIN
set #pos = #pos + #i
SELECT #NewStr = SUBSTRING(#NewStr, #i + 1, LEN(#S))
--SELECT #NewStr '#NewStr', #Char '#Char', #pos '#pos', #sql '#sql'
--SELECT #NewStr '#NewStr', #pos '#pos'
INSERT INTO #D
SELECT #NewStr, #pos
SET #i = #i + 1
END
ELSE
BREAK
END
SELECT * FROM #D
END
If you're using MySQL and cannot use the instr function that accepts four parameters or regexp_substr, you can do this way:
select substring_index(substring_index(help, ' ', 2), ' ', -1)
from (select 'hello my name is...' as help) h
Result: "my".
Replace "2" in the code above with the number of the word you want.
If you are using SQL Server 2016+ then you can take advantage of the STRING_SPLIT function. It returns rows of string values and if you aim to get nth value, then you can use Row_Number() window function.
Here there is a little trick as you don't want to really order by something so that you have to "cheat" the row_number function and allow its value in the natural order which is the STRING_SPLIT() function will spit out.
Below is a code snippet if you want to find the third word of the string
Declare #_intPart INT = 3; -- change nth work here, start # from 1 not 0
SELECT value FROM(
SELECT value,
ROW_NUMBER()OVER(ORDER BY (SELECT 1)) AS rowno
FROM STRING_SPLIT('hello world this is amazing', ' ')
) AS o1 WHERE o1.rowno = #_intPart;
You can also make a scalar function to retrieve values.
I'm looking to pull floats out of some varchars, using PATINDEX() to spot them. I know in each varchar string, I'm only interested in the first float that exists, but they might have different lengths.
e.g.
'some text 456.09 other text'
'even more text 98273.453 la la la'
I would normally match these with a regex
"[0-9]+[.][0-9]+"
However, I can't find an equivalent for the + operator, which PATINDEX accepts. So they would need to be matched (respectively) with:
'[0-9][0-9][0-9].[0-9][0-9]' and '[0-9][0-9][0-9][0-9][0-9].[0-9][0-9][0-9]'
Is there any way to match both of these example varchars with one single valid PATINDEX pattern?
I blogged about this a while ago.
Extracting numbers with SQL server
Declare #Temp Table(Data VarChar(100))
Insert Into #Temp Values('some text 456.09 other text')
Insert Into #Temp Values('even more text 98273.453 la la la')
Insert Into #Temp Values('There are no numbers in this one')
Select Left(
SubString(Data, PatIndex('%[0-9.-]%', Data), 8000),
PatIndex('%[^0-9.-]%', SubString(Data, PatIndex('%[0-9.-]%', Data), 8000) + 'X')-1)
From #Temp
Wildcards.
SELECT PATINDEX('%[0-9]%[0-9].[0-9]%[0-9]%','some text 456.09 other text')
SELECT PATINDEX('%[0-9]%[0-9].[0-9]%[0-9]%','even more text 98273.453 la la la')
Yes you need to link to the clr to get regex support. But if PATINDEX does not do what you need then regex was designed exactly for that.
http://msdn.microsoft.com/en-us/magazine/cc163473.aspx
Should be checked for robustness (what if you only have an int, for example), but this is just to put you on a track:
if exists (select routine_name from information_schema.routines where routine_name = 'GetFirstFloat')
drop function GetFirstFloat
go
create function GetFirstFloat (#string varchar(max))
returns float
as
begin
declare #float varchar(max)
declare #pos int
select #pos = patindex('%[0-9]%', #string)
select #float = ''
while isnumeric(substring(#string, #pos, 1)) = 1
begin
select #float = #float + substring(#string, #pos, 1)
select #pos = #pos + 1
end
return cast(#float as float)
end
go
select dbo.GetFirstFloat('this is a string containing pi 3.14159216 and another non float 3 followed by a new fload 5.41 and that''s it')
select dbo.GetFirstFloat('this is a string with no float')
select dbo.GetFirstFloat('this is another string with an int 3')
Given that the pattern is going to be varied in length, you're not going to have a rough time getting this to work with PATINDEX. There is another post that I wrote, which I've modified to accomplish what you're trying to do here. Will this work for you?
CREATE TABLE #nums (n INT)
DECLARE #i INT
SET #i = 1
WHILE #i < 8000
BEGIN
INSERT #nums VALUES(#i)
SET #i = #i + 1
END
CREATE TABLE #tmp (
id INT IDENTITY(1,1) not null,
words VARCHAR(MAX) null
)
INSERT INTO #tmp
VALUES('I''m looking for a number, regardless of length, even 23.258 long'),('Maybe even pi which roughly 3.14159265358,'),('or possibly something else that isn''t a number')
UPDATE #tmp SET words = REPLACE(words, ',',' ')
;WITH CTE AS (SELECT ROW_NUMBER() OVER (ORDER BY ID) AS rownum, ID, NULLIF(SUBSTRING(' ' + words + ' ' , n , CHARINDEX(' ' , ' ' + words + ' ' , n) - n) , '') AS word
FROM #nums, #tmp
WHERE ID <= LEN(' ' + words + ' ') AND SUBSTRING(' ' + words + ' ' , n - 1, 1) = ' '
AND CHARINDEX(' ' , ' ' + words + ' ' , n) - n > 0),
ids AS (SELECT ID, MIN(rownum) AS rownum FROM CTE WHERE ISNUMERIC(word) = 1 GROUP BY id)
SELECT CTE.rownum, cte.id, cte.word
FROM CTE, ids WHERE cte.id = ids.id AND cte.rownum = ids.rownum
The explanation and origin of the code is covered in more detail in the origional post
PATINDEX is not powerful enough to do that. You should use regular expressions.
SQL Server has Regular expression support since SQL Server 2005.
I am trying to count words of text that is written in a column of table. Therefor I am using the following query.
SELECT LEN(ExtractedText) -
LEN(REPLACE(ExtractedText, ' ', '')) + 1 from EDDSDBO.Document where ID='100'.
I receive a wrong result that is much to high.
On the other hand, if I copy the text directly into the statement then it works, i.e.
SELECT LEN('blablabla text') - LEN(REPLACE('blablabla text', ' ', '')) + 1.
Now the datatype is nvarchar(max) since the text is very long. I have already tried to convert the column into text or ntext and to apply datalength() instead of len(). Nevertheless I obtain the same result that it does work as a string but does not work from a table.
You're counting spaces not words. That will typically yield an approximate answer.
e.g.
' this string will give an incorrect result '
Try this approach: http://www.sql-server-helper.com/functions/count-words.aspx
CREATE FUNCTION [dbo].[WordCount] ( #InputString VARCHAR(4000) )
RETURNS INT
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #WordCount INT
SET #Index = 1
SET #WordCount = 0
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar = ' ' AND #Char != ' '
SET #WordCount = #WordCount + 1
SET #Index = #Index + 1
END
RETURN #WordCount
END
GO
usage
DECLARE #String VARCHAR(4000)
SET #String = 'Health Insurance is an insurance against expenses incurred through illness of the insured.'
SELECT [dbo].[WordCount] ( #String )
Leading spaces, trailing spaces, two or more spaces between the neighbouring words – these are the likely causes of the wrong results you are getting.
The functions LTRIM() and RTRIM() can help you eliminate the first two issues. As for the third one, you can use REPLACE(ExtractedText, ' ', ' ') to replace double spaces with single ones, but I'm not sure if you do not have triple ones (in which case you'd need to repeat the replacing).
UPDATE
Here's a UDF that uses CTEs and ranking to eliminate extra spaces and then counts the remaining ones to return the quantity as the number of words:
CREATE FUNCTION fnCountWords (#Str varchar(max))
RETURNS int
AS BEGIN
DECLARE #xml xml, #res int;
SET #Str = RTRIM(LTRIM(#Str));
WITH split AS (
SELECT
idx = number,
chr = SUBSTRING(#Str, number, 1)
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND LEN(#Str)
),
ranked AS (
SELECT
idx,
chr,
rnk = idx - ROW_NUMBER() OVER (PARTITION BY chr ORDER BY idx)
FROM split
)
SELECT #res = COUNT(DISTINCT rnk) + 1
FROM ranked
WHERE chr = ' ';
RETURN #res;
END
With this function your query will be simply like this:
SELECT fnCountWords(ExtractedText)
FROM EDDSDBO.Document
WHERE ID='100'
UPDATE 2
The function uses one of the system tables, master..spt_values, as a tally table. The particular subset used contains only values from 0 to 2047. This means the function will not work correctly for inputs longer than 2047 characters (after trimming both leading and trailing spaces), as #t-clausen.dk has correctly noted in his comment. Therefore, a custom tally table should be used if longer input strings are possible.
Replace the spaces with something that never occur in your text like ' $!' or pick another value.
then replace all '$! ' and '$!' with nothing this way you never have more than 1 space after a word. Then use your current script. I have defined a word as a space followed by a non-space.
This is an example
DECLARE #T TABLE(COL1 NVARCHAR(2000), ID INT)
INSERT #T VALUES('A B C D', 100)
SELECT LEN(C) - LEN(REPLACE(C,' ', '')) COUNT FROM (
SELECT REPLACE(REPLACE(REPLACE(' ' + COL1, ' ', ' $!'), '$! ',''), '$!', '') C
FROM #T ) A
Here is a recursive solution
DECLARE #T TABLE(COL1 NVARCHAR(2000), ID INT)
INSERT #T VALUES('A B C D', 100)
INSERT #T VALUES('have a nice day with 7 words', 100)
;WITH CTE AS
(
SELECT 1 words, col1 c, col1 FROM #t WHERE id = 100
UNION ALL
SELECT words +1, right(c, len(c) - patindex('% [^ ]%', c)), col1 FROM cte
WHERE patindex('% [^ ]%', c) > 0
)
SELECT words, col1 FROM cte WHERE patindex('% [^ ]%', c) = 0
You should declare the column using the varchar data type, like:
create table emp(ename varchar(22));
insert into emp values('amit');
select ename,len(ename) from emp;
output : 4