I have a column that has values formatted like a,b,c,d. Is there a way to count the number of commas in that value in T-SQL?
24 Answers
The first way that comes to mind is to do it indirectly by replacing the comma with an empty string and comparing the lengths
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))

- 56,771
- 11
- 70
- 62
-
15That answers the question as written in the text, but not as written in the title. To make it work for more than one character, just need to add a / len(searchterm) round the thing. Posted an answer in-case it's useful for someone. – Andrew Barrett May 25 '10 at 15:55
-
Someone pointed out to me that this doesn't always work as expected. Consider the following: SELECT LEN('a,b,c,d ,') - LEN(REPLACE('a,b,c,d ,', ',', '')) For reasons I don't yet understand, the space between the d and the final column causes this to return 5 instead of 4. I will post another answer which fixes this, in case it is useful to anyone. – bubbleking Apr 30 '15 at 15:54
-
5Maybe using DATALENGTH instead LEN would be better, because LEN returns the size of the string trimmed. – rodrigocl Nov 03 '15 at 17:26
-
2DATALENGTH()/2 is also tricky because of non-obvious char sizes. Look at stackoverflow.com/a/11080074/1094048 for simple and accurate way to get string length. – pkuderov Jul 13 '16 at 13:51
-
@rodrigocl Why not wrap an `LTRIM` around the string as follows: `SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))`? – Alex Bello Jan 12 '17 at 15:30
-
this is correct, if you need to search a string having 2 or more character, you can enhance the formula like this (len(@string) - len(replace(@string, @substring, '')))/len(@substring) – Imran Rizvi Nov 23 '17 at 10:07
-
@ImranRizvi has a great answer for counting strings of any size. It can even be enhanced further to also allow you to **count spaces** or **substrings with leading/trailing spaces** by replacing LEN with DATALENGTH like this: `(DATALENGTH(@string) - DATALENGTH(REPLACE(@string, @substring, '')))/DATALENGTH(@substring)` – rbsdca Feb 22 '18 at 07:37
Quick extension of cmsjr's answer that works for strings with more than one character.
CREATE FUNCTION dbo.CountOccurrencesOfString
(
@searchString nvarchar(max),
@searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
return (LEN(@searchString)-LEN(REPLACE(@searchString,@searchTerm,'')))/LEN(@searchTerm)
END
Usage:
SELECT * FROM MyTable
where dbo.CountOccurrencesOfString(MyColumn, 'MyString') = 1

- 7
- 2

- 19,721
- 4
- 47
- 52
-
21A slight improvement would be to use DATALENGTH()/2 instead of LEN(). LEN will ignore any trailing whitespace so `dbo.CountOccurancesOfString( 'blah ,', ',')` will return 2 instead of 1 and `dbo.CountOccurancesOfString( 'hello world', ' ')` will fail with divide by zero. – Rory Jun 29 '12 at 10:25
-
6Rory's comment is helpful. I found that I could just replace LEN with DATALENGTH in Andrew's function and get the desired result. It seems that dividing by 2 is not necessary with the way the math works out. – Garland Pope Mar 28 '13 at 15:11
-
@AndrewBarrett : What append when several strings have the same length? – user2284570 May 12 '14 at 15:57
-
2`DATALENGTH()/2` is also tricky because of non-obvious char sizes. Look at http://stackoverflow.com/a/11080074/1094048 for simple and accurate way. – pkuderov Jul 13 '16 at 13:49
You can compare the length of the string with one where the commas are removed:
len(value) - len(replace(value,',',''))

- 687,336
- 108
- 737
- 1,005
The answer by @csmjr has a problem in some instances.
His answer was to do this:
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
This works in most scenarios, however, try running this:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))
For some reason, REPLACE gets rid of the final comma but ALSO the space just before it (not sure why). This results in a returned value of 5 when you'd expect 4. Here is another way to do this which will work even in this special scenario:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)
Note that you don't need to use asterisks. Any two-character replacement will do. The idea is that you lengthen the string by one character for each instance of the character you're counting, then subtract the length of the original. It's basically the opposite method of the original answer which doesn't come with the strange trimming side-effect.

- 3,329
- 3
- 29
- 49
-
6"For some reason, REPLACE gets rid of the final comma but ALSO the space just before it (not sure why)." REPLACE ain't getting rid of the last comma and the space before it, it's actually the LEN function that's ignoring the white-space resulting at the end of the string because of that space. – Imranullah Khan May 21 '15 at 11:56
Building on @Andrew's solution, you'll get much better performance using a non-procedural table-valued-function and CROSS APPLY:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/* Usage:
SELECT t.[YourColumn], c.StringCount
FROM YourDatabase.dbo.YourTable t
CROSS APPLY dbo.CountOccurrencesOfString('your search string', t.[YourColumn]) c
*/
CREATE FUNCTION [dbo].[CountOccurrencesOfString]
(
@searchTerm nvarchar(max),
@searchString nvarchar(max)
)
RETURNS TABLE
AS
RETURN
SELECT (DATALENGTH(@searchString)-DATALENGTH(REPLACE(@searchString,@searchTerm,'')))/NULLIF(DATALENGTH(@searchTerm), 0) AS StringCount

- 249
- 1
- 3
- 11

- 5,273
- 1
- 24
- 28
-
I use this same function in many of my legacy databases, it helps a great deal with a lot of old and improperly designed databases. Saves a great deal of time and is very fast even on large data sets. – Caimen Oct 28 '16 at 17:02
Declare @string varchar(1000)
DECLARE @SearchString varchar(100)
Set @string = 'as as df df as as as'
SET @SearchString = 'as'
select ((len(@string) - len(replace(@string, @SearchString, ''))) -(len(@string) -
len(replace(@string, @SearchString, ''))) % 2) / len(@SearchString)

- 15,729
- 10
- 59
- 55

- 29
- 2
Accepted answer is correct , extending it to use 2 or more character in substring:
Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)

- 7,331
- 11
- 57
- 101
Darrel Lee I think has a pretty good answer. Replace CHARINDEX()
with PATINDEX()
, and you can do some weak regex
searching along a string, too...
Like, say you use this for @pattern
:
set @pattern='%[-.|!,'+char(9)+']%'
Why would you maybe want to do something crazy like this?
Say you're loading delimited text strings into a staging table, where the field holding the data is something like a varchar(8000) or nvarchar(max)...
Sometimes it's easier/faster to do ELT (Extract-Load-Transform) with data rather than ETL (Extract-Transform-Load), and one way to do this is to load the delimited records as-is into a staging table, especially if you may want an simpler way to see the exceptional records rather than deal with them as part of an SSIS package...but that's a holy war for a different thread.

- 5,690
- 6
- 40
- 58

- 676
- 6
- 5
If we know there is a limitation on LEN and space, why cant we replace the space first? Then we know there is no space to confuse LEN.
len(replace(@string, ' ', '-')) - len(replace(replace(@string, ' ', '-'), ',', ''))

- 855
- 10
- 11
Use this code, it is working perfectly. I have create a sql function that accept two parameters, the first param is the long string that we want to search into it,and it can accept string length up to 1500 character(of course you can extend it or even change it to text datatype). And the second parameter is the substring that we want to calculate the number of its occurance(its length is up to 200 character, of course you can change it to what your need). and the output is an integer, represent the number of frequency.....enjoy it.
CREATE FUNCTION [dbo].[GetSubstringCount]
(
@InputString nvarchar(1500),
@SubString NVARCHAR(200)
)
RETURNS int
AS
BEGIN
declare @K int , @StrLen int , @Count int , @SubStrLen int
set @SubStrLen = (select len(@SubString))
set @Count = 0
Set @k = 1
set @StrLen =(select len(@InputString))
While @K <= @StrLen
Begin
if ((select substring(@InputString, @K, @SubStrLen)) = @SubString)
begin
if ((select CHARINDEX(@SubString ,@InputString)) > 0)
begin
set @Count = @Count +1
end
end
Set @K=@k+1
end
return @Count
end

- 31
- 3
In SQL 2017 or higher, you can use this:
declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)

- 1,448
- 14
- 15
Improved version based on top answer and other answers:
Wrapping the string with delimiters ensures that LEN works properly. Making the replace character string one character longer than the match string removes the need for division.
CREATE FUNCTION dbo.MatchCount(@value nvarchar(max), @match nvarchar(max))
RETURNS int
BEGIN
RETURN LEN('[' + REPLACE(@value,@match,REPLICATE('*', LEN('[' + @match + ']') - 1)) + ']') - LEN('['+@value+']')
END

- 609
- 4
- 7
The following should do the trick for both single character and multiple character searches:
CREATE FUNCTION dbo.CountOccurrences
(
@SearchString VARCHAR(1000),
@SearchFor VARCHAR(1000)
)
RETURNS TABLE
AS
RETURN (
SELECT COUNT(*) AS Occurrences
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY O.object_id) AS n
FROM sys.objects AS O
) AS N
JOIN (
VALUES (@SearchString)
) AS S (SearchString)
ON
SUBSTRING(S.SearchString, N.n, LEN(@SearchFor)) = @SearchFor
);
GO
---------------------------------------------------------------------------------------
-- Test the function for single and multiple character searches
---------------------------------------------------------------------------------------
DECLARE @SearchForComma VARCHAR(10) = ',',
@SearchForCharacters VARCHAR(10) = 'de';
DECLARE @TestTable TABLE
(
TestData VARCHAR(30) NOT NULL
);
INSERT INTO @TestTable
(
TestData
)
VALUES
('a,b,c,de,de ,d e'),
('abc,de,hijk,,'),
(',,a,b,cde,,');
SELECT TT.TestData,
CO.Occurrences AS CommaOccurrences,
CO2.Occurrences AS CharacterOccurrences
FROM @TestTable AS TT
OUTER APPLY dbo.CountOccurrences(TT.TestData, @SearchForComma) AS CO
OUTER APPLY dbo.CountOccurrences(TT.TestData, @SearchForCharacters) AS CO2;
The function can be simplified a bit using a table of numbers (dbo.Nums):
RETURN (
SELECT COUNT(*) AS Occurrences
FROM dbo.Nums AS N
JOIN (
VALUES (@SearchString)
) AS S (SearchString)
ON
SUBSTRING(S.SearchString, N.n, LEN(@SearchFor)) = @SearchFor
);

- 1
- 1
I finally write this function that should cover all the possible situations, adding a char prefix and suffix to the input. this char is evaluated to be different to any of the char conteined in the search parameter, so it can't affect the result.
CREATE FUNCTION [dbo].[CountOccurrency]
(
@Input nvarchar(max),
@Search nvarchar(max)
)
RETURNS int AS
BEGIN
declare @SearhLength as int = len('-' + @Search + '-') -2;
declare @conteinerIndex as int = 255;
declare @conteiner as char(1) = char(@conteinerIndex);
WHILE ((CHARINDEX(@conteiner, @Search)>0) and (@conteinerIndex>0))
BEGIN
set @conteinerIndex = @conteinerIndex-1;
set @conteiner = char(@conteinerIndex);
END;
set @Input = @conteiner + @Input + @conteiner
RETURN (len(@Input) - len(replace(@Input, @Search, ''))) / @SearhLength
END
usage
select dbo.CountOccurrency('a,b,c,d ,', ',')

- 11
- 4
Declare @MainStr nvarchar(200)
Declare @SubStr nvarchar(10)
Set @MainStr = 'nikhildfdfdfuzxsznikhilweszxnikhil'
Set @SubStr = 'nikhil'
Select (Len(@MainStr) - Len(REPLACE(@MainStr,@SubStr,'')))/Len(@SubStr)

- 29
- 2
this T-SQL code finds and prints all occurrences of pattern @p in sentence @s. you can do any processing on the sentence afterward.
declare @old_hit int = 0
declare @hit int = 0
declare @i int = 0
declare @s varchar(max)='alibcalirezaalivisualization'
declare @p varchar(max)='ali'
while @i<len(@s)
begin
set @hit=charindex(@p,@s,@i)
if @hit>@old_hit
begin
set @old_hit =@hit
set @i=@hit+1
print @hit
end
else
break
end
the result is: 1 6 13 20

- 355
- 2
- 6
I ended up using a CTE table for this,
CREATE TABLE #test (
[id] int,
[field] nvarchar(500)
)
INSERT INTO #test ([id], [field])
VALUES (1, 'this is a test string http://url, and https://google.com'),
(2, 'another string, hello world http://example.com'),
(3, 'a string with no url')
SELECT *
FROM #test
;WITH URL_count_cte ([id], [url_index], [field])
AS
(
SELECT [id], CHARINDEX('http', [field], 0)+1 AS [url_index], [field]
FROM #test AS [t]
WHERE CHARINDEX('http', [field], 0) != 0
UNION ALL
SELECT [id], CHARINDEX('http', [field], [url_index])+1 AS [url_index], [field]
FROM URL_count_cte
WHERE CHARINDEX('http', [field], [url_index]) > 0
)
-- total urls
SELECT COUNT(1)
FROM URL_count_cte
-- urls per row
SELECT [id], COUNT(1) AS [url_count]
FROM URL_count_cte
GROUP BY [id]

- 787
- 2
- 12
- 35
Using this function, you can get the number of repetitions of words in a text.
/****** Object: UserDefinedFunction [dbo].[fn_getCountKeywords] Script Date: 22/11/2021 17:52:00 ******/
DROP FUNCTION IF EXISTS [dbo].[fn_getCountKeywords]
GO
/****** Object: UserDefinedFunction [dbo].[fn_getCountKeywords] Script Date: 2211/2021 17:52:00 ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: m_Khezrian
-- Create date: 2021/11/22-17:52
-- Description: Return Count Keywords In Input Text
-- =============================================
Create OR Alter Function [dbo].[fn_getCountKeywords]
(@Text nvarchar(max)
,@Keywords nvarchar(max)
)
RETURNS @Result TABLE
(
[ID] int Not Null IDENTITY PRIMARY KEY
,[Keyword] nvarchar(max) Not Null
,[Cnt] int Not Null Default(0)
)
/*With ENCRYPTION*/ As
Begin
Declare @Key nvarchar(max);
Declare @Cnt int;
Declare @I int;
Set @I = 0 ;
--Set @Text = QUOTENAME(@Text);
Insert Into @Result
([Keyword])
Select Trim([value])
From String_Split(@Keywords,N',')
Group By [value]
Order By Len([value]) Desc;
Declare CntKey_Cursor Insensitive Cursor For
Select [Keyword]
From @Result
Order By [ID];
Open CntKey_Cursor;
Fetch Next From CntKey_Cursor Into @Key;
While (@@Fetch_STATUS = 0) Begin
Set @Cnt = 0;
While (PatIndex(N'%'+@Key+'%',@Text) > 0) Begin
Set @Cnt += 1;
Set @I += 1 ;
Set @Text = Stuff(@Text,PatIndex(N'%'+@Key+'%',@Text),len(@Key),N'{'+Convert(nvarchar,@I)+'}');
--Set @Text = Replace(@Text,@Key,N'{'+Convert(nvarchar,@I)+'}');
End--While
Update @Result
Set [Cnt] = @Cnt
Where ([Keyword] = @Key);
Fetch Next From CntKey_Cursor Into @Key;
End--While
Close CntKey_Cursor;
Deallocate CntKey_Cursor;
Return
End
GO
--Test
Select *
From dbo.fn_getCountKeywords(
N'<U+0001F4E3> MARKET IMPACT Euro area Euro CPIarea annual inflation up to 3.0% MaCPIRKET forex'
,N'CPI ,core,MaRKET , Euro area'
)
Go

- 57
- 3
Example:
SELECT s.*
,s.[Number1] - (SELECT COUNT(Value)
FROM string_split(s.[StringColumn],',')
WHERE RTRIM(VALUE) <> '')
FROM TableName AS s
Applies to: SQL Server 2016 (13.x) and later

- 1
- 2
-
I don't know what `s.[Number1]` is, but if you subtract 1 from the remainder of your query you get the correct answer – K Scandrett Jul 16 '23 at 13:20
DECLARE @INPUT VARCHAR(70)='Lorem ipsum dolor sit amet.'
SELECT COUNT(*)-1 AS COUNT_OF_SPACES FROM
(SELECT value FROM STRING_SPLIT(@INPUT ,' ')) T

- 11,977
- 56
- 49
- 78

- 1
- 2
You can use the following stored procedure to fetch , values.
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[sp_parsedata]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[sp_parsedata]
GO
create procedure sp_parsedata
(@cid integer,@st varchar(1000))
as
declare @coid integer
declare @c integer
declare @c1 integer
select @c1=len(@st) - len(replace(@st, ',', ''))
set @c=0
delete from table1 where complainid=@cid;
while (@c<=@c1)
begin
if (@c<@c1)
begin
select @coid=cast(replace(left(@st,CHARINDEX(',',@st,1)),',','') as integer)
select @st=SUBSTRING(@st,CHARINDEX(',',@st,1)+1,LEN(@st))
end
else
begin
select @coid=cast(@st as integer)
end
insert into table1(complainid,courtid) values(@cid,@coid)
set @c=@c+1
end
-
line 4 of this stored procedure sets `@c1` to the answer that he requires. What use is the rest of the code, considering that it needs a pre existing table called `table1` to work, has a hard coded delimeter, and cannot be used inline like the accepted answer from two months prior? – Nick.Mc Aug 14 '14 at 02:57
The Replace/Len test is cute, but probably very inefficient (especially in terms of memory). A simple function with a loop will do the job.
CREATE FUNCTION [dbo].[fn_Occurences]
(
@pattern varchar(255),
@expression varchar(max)
)
RETURNS int
AS
BEGIN
DECLARE @Result int = 0;
DECLARE @index BigInt = 0
DECLARE @patLen int = len(@pattern)
SET @index = CHARINDEX(@pattern, @expression, @index)
While @index > 0
BEGIN
SET @Result = @Result + 1;
SET @index = CHARINDEX(@pattern, @expression, @index + @patLen)
END
RETURN @Result
END

- 2,372
- 22
- 22
-
Across any table of appreciable size, using a procedural function is _far_ more inefficient – Nick.Mc Aug 14 '14 at 02:58
-
Good point. Are the built Len call much faster then a use defined function? – Darrel Lee Dec 19 '14 at 23:11
-
At a large scale of records, yes. Though to be certain you'd have to test on a large recordset with large strings. Never write anything procedural in SQL if you can avoid it (i.e. loops) – Nick.Mc Dec 20 '14 at 05:06
Perhaps you should not store data that way. It is a bad practice to ever store a comma delimited list in a field. IT is very inefficient for querying. This should be a related table.

- 94,695
- 15
- 113
- 186
-
+1 for thinking of that. It's what I usually start with when someone uses comma separated data in a field. – Guffa Apr 10 '09 at 20:03
-
6Part of the purpose of this question was to take existing data like that and split it apart appropriately. – Orion Adrian Jun 03 '10 at 18:34
-
7Some of us are given legacy databases where that was done and we can't do anything about it. – eddieroger Mar 10 '14 at 14:33
-
@Mulmoth, of course it is an answer. you fix the problem not the symptom. The problem is withthe database design. – HLGEM May 08 '14 at 13:45
-
2@HLGEM The question *may* point to a problem, but it can be understood more general. The question is totally legitimate for very well normalized databases. – Zeemee May 08 '14 at 14:13
-
Totally agree with @HLGEM, that data in delimated string is a bad practice but there are different scenarios where the relational database fails so there is really a need for doing this in case when you don't have an option of thinking without rdbms... – Dragon Oct 22 '15 at 17:16
-
@Zeemee I think balancing may say in this case, that although it is not a literal answer to the original question, the truth in the post is so strong, so obvious, that it deserves an up. – peterh Dec 30 '17 at 19:41