SQL varchar에서 특정 하위 문자열의 발생 횟수를 어떻게 계산합니까?


150

a, b, c, d와 같은 형식의 값을 가진 열이 있습니다. T-SQL에서 해당 값의 쉼표 수를 계산하는 방법이 있습니까?

답변:


245

가장 먼저 떠오르는 방법은 쉼표를 빈 문자열로 바꾸고 길이를 비교하여 간접적으로 수행하는 것입니다.

Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))

13
그것은 제목에 쓰여진 것이 아니라 텍스트에 쓰여진 질문에 대한 답입니다. 둘 이상의 문자에 적용하려면 / len (searchterm)을 추가하면됩니다. 경우에 따라 답변을 게시하면 누군가에게 유용합니다.
Andrew Barrett

누군가 나에게 이것이 항상 예상대로 작동하지는 않는다고 지적했습니다. 다음을 고려하십시오. SELECT LEN ( 'a, b, c, d,')-LEN (REPLACE ( 'a, b, c, d,', ',', '')) 아직 이해하지 못하는 이유로 , d와 마지막 열 사이의 공백으로 인해 4 대신 5가 반환됩니다. 다른 사람에게 유용한 경우이 문제를 해결하는 다른 답변을 게시합니다.
bubbleking

5
LEN 대신 트리밍 된 문자열의 크기를 반환하므로 DATALENGTH를 대신 사용하면 LEN이 더 좋습니다.
rodrigocl

2
명확하지 않은 문자 크기 때문에 DATALENGTH () / 2도 까다 롭습니다. 문자열 길이를 얻는 간단하고 정확한 방법은 stackoverflow.com/a/11080074/1094048을 참조하십시오.
pkuderov

@rodrigocl 왜 LTRIM문자열을 다음과 같이 감싸지 않겠습니까 SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))?
Alex Bello

67

더 많은 문자의 문자열에 작동하는 cmsjr의 답변을 빠르게 확장합니다.

CREATE FUNCTION dbo.CountOccurrencesOfString
(
    @searchString nvarchar(max),
    @searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
    return (LEN(@searchString)-LEN(REPLACE(@searchString,@searchTerm,'')))/LEN(@searchTerm)
END

용법:

SELECT * FROM MyTable
where dbo.CountOccurrencesOfString(MyColumn, 'MyString') = 1

16
LEN () 대신 DATALENGTH () / 2를 사용하는 것이 약간 개선되었습니다. LEN은 후행 공백을 무시하므로 dbo.CountOccurancesOfString( 'blah ,', ',')1 대신 2를 반환 dbo.CountOccurancesOfString( 'hello world', ' ')하고 0으로 나누면 실패합니다.
Rory

5
Rory의 의견이 도움이됩니다. Andrew의 함수에서 LEN을 DATALENGTH로 바꾸고 원하는 결과를 얻을 수 있음을 알았습니다. 수학이 수행되는 방식에 2로 나누는 것이 필요하지 않은 것 같습니다.
Garland Pope

@AndrewBarrett : 여러 문자열의 길이가 같은 경우 어떻게 추가됩니까?
user2284570

2
DATALENGTH()/2명확하지 않은 문자 크기 때문에 까다 롭습니다. 간단하고 정확한 방법 은 stackoverflow.com/a/11080074/1094048 을 참조하십시오.
pkuderov

26

문자열의 길이를 쉼표가 제거 된 길이와 비교할 수 있습니다.

len(value) - len(replace(value,',',''))

8

@Andrew의 솔루션을 기반으로 비 절차 적 테이블 반환 함수와 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

나는 많은 레거시 데이터베이스에서 이와 동일한 기능을 사용하며, 오래되고 부적절하게 설계된 많은 데이터베이스를 처리하는 데 큰 도움이됩니다. 많은 시간을 절약하고 대용량 데이터 세트에서도 매우 빠릅니다.
Caimen

6

@csmjr의 답변은 경우에 따라 문제가 있습니다.

그의 대답은 다음과 같습니다.

Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))

이것은 대부분의 시나리오에서 작동하지만 다음을 실행하십시오.

DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))

어떤 이유로 REPLACE는 최종 쉼표를 제거하지만 바로 앞의 공백을 제거합니다 (이유는 확실하지 않습니다). 4를 기대할 때 리턴 값은 5입니다.이 특별한 시나리오에서도 작동하는 다른 방법이 있습니다.

DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)

별표를 사용할 필요는 없습니다. 두 문자로 대체됩니다. 계산하는 캐릭터의 각 인스턴스에 대해 문자열을 한 문자 씩 늘리고 원본의 길이를 빼는 것이 좋습니다. 기본적으로 이상한 트리밍 부작용이없는 원래 답변의 반대 방법입니다.


5
"어떤 이유로 REPLACE는 최종 쉼표를 제거하지만 바로 앞의 공백도 제거합니다 (이유는 확실하지 않습니다)." REPLACE는 마지막 쉼표와 그 앞의 공백을 제거하지 않습니다. 실제로 공백 때문에 문자열의 끝에서 발생하는 공백을 무시하는 LEN 함수입니다.
Imranullah Khan

2
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)

이것은 실제로 실제 개수보다 적은 1을 반환합니다.
Integrator

1

허용되는 답변은 올 바르며 하위 문자열에 2 개 이상의 문자를 사용하도록 확장됩니다.

Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)

1

LEN과 공간에 제한이 있다는 것을 알고 있다면 왜 공간을 먼저 교체 할 수 없습니까? 그런 다음 LEN을 혼동 할 공간이 없다는 것을 알고 있습니다.

len(replace(@string, ' ', '-')) - len(replace(replace(@string, ' ', '-'), ',', ''))

0
DECLARE @records varchar(400)
SELECT @records = 'a,b,c,d'
select  LEN(@records) as 'Before removing Commas' , LEN(@records) - LEN(REPLACE(@records, ',', '')) 'After Removing Commans'

0

대럴 리 꽤 좋은 답변이 있다고 생각합니다. 교체 CHARINDEX()PATINDEX(), 당신은 몇 가지 약을 할 수 regex도 캐릭터에 따라 검색을 ...

마찬가지로 이것을 사용한다고 가정 해보십시오 @pattern.

set @pattern='%[-.|!,'+char(9)+']%'

왜 이런 미친 짓을하고 싶습니까?

구분 된 텍스트 문자열을 준비 테이블에로드한다고 가정합니다. 여기서 데이터를 보유하는 필드는 varchar (8000) 또는 nvarchar (max)와 같습니다.

때때로 ETL (Extract-Transform-Load)이 아닌 데이터를 사용하여 ELT (Extract-Load-Transform)를 수행하는 것이 더 쉽고 빠르며,이를 수행하는 한 가지 방법은 구분 된 레코드를 스테이징 테이블에 그대로로드하는 것입니다. SSIS 패키지의 일부로 다루지 않고 예외적 인 레코드를 볼 수있는 더 간단한 방법을 원할 수도 있습니다. 그러나 그것은 다른 스레드와의 전쟁입니다.


0

다음은 단일 문자 검색과 다중 문자 검색에 대한 트릭을 수행해야합니다.

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;

이 함수는 숫자 테이블 (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
          );

0

이 코드를 사용하면 완벽하게 작동합니다. 두 개의 매개 변수를 허용하는 sql 함수를 만들었습니다. 첫 번째 매개 변수는 검색하려는 긴 문자열이며 최대 1500 자의 문자열 길이를 허용 할 수 있습니다 (물론 확장하거나 텍스트 데이터 유형으로 변경할 수도 있음) ). 그리고 두 번째 매개 변수는 발생 횟수를 계산하려는 하위 문자열입니다 (길이는 최대 200 자입니다. 물론 필요에 따라 변경할 수 있습니다). 그리고 출력은 정수이고, 주파수의 수를 나타냅니다 ..... 즐길 수 있습니다.


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

0

마지막으로 입력에 char 접두사와 접미사를 추가하여 가능한 모든 상황을 다루어야하는이 함수를 작성하십시오. 이 문자는 검색 매개 변수에 포함 된 문자와 다른 것으로 평가되므로 결과에 영향을 미치지 않습니다.

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 

용법

select dbo.CountOccurrency('a,b,c,d ,', ',')

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

0

SQL 2017 이상에서는 다음을 사용할 수 있습니다.

declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)

0

이 T-SQL 코드는 문장 @s에서 패턴 @p를 모두 찾아서 인쇄합니다. 나중에 문장에 대한 처리를 수행 할 수 있습니다.

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

결과 : 1 6 13 20


0

SQL Server 2017 용

declare @hits int = 0;
set @hits = (select count(*) from (select value from STRING_SPLIT('F609,4DFA,8499',',')) a);
select @hits;

-1

다음 저장 프로 시저를 사용하여 값을 가져올 수 있습니다.

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

이 저장 프로 시저의 4 번째 줄은 @c1필요한 답변으로 설정 됩니다. table1작동하도록 호출 된 기존 테이블이 필요 하고 하드 코드 된 델리 미터가 있으며 2 개월 전의 승인 된 답변처럼 인라인으로 사용할 수 없다는 점을 고려할 때 나머지 코드는 어떤 용도 로 사용됩니까?
Nick.McDermaid 2

-1

Replace / Len 테스트는 귀엽지 만 매우 비효율적 일 수 있습니다 (특히 메모리 측면에서). 루프가있는 간단한 함수가 작업을 수행합니다.

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

절차 적 기능을 사용하면 크기가 큰 테이블에서 훨씬 비효율적입니다.
Nick.McDermaid

좋은 지적. 빌드 된 Len 호출이 사용 정의 함수보다 훨씬 빠릅니까?
Darrel Lee

대규모 기록에서 그렇습니다. 확실하지만 큰 문자열이있는 큰 레코드 세트에서 테스트해야합니다. 피할 수 있다면 SQL에 절차적인 내용을 쓰지 마십시오 (예 : 루프)
Nick.McDermaid

-3

아마도 그런 식으로 데이터를 저장해서는 안됩니다. 필드에 쉼표로 구분 된 목록을 저장하는 것은 좋지 않습니다. IT는 쿼리에 매우 비효율적입니다. 관련 테이블이어야합니다.


그걸 생각하면 +1입니다. 누군가가 필드에서 쉼표로 구분 된 데이터를 사용할 때 일반적으로 시작하는 것입니다.
Guffa

6
이 질문의 목적 중 일부는 기존 데이터를 가져 와서 적절히 분리하는 것이 었습니다.
오리온 아드리안

7
우리 중 일부는 레거시 데이터베이스를 제공 받았으며 그에 대해서는 아무것도 할 수 없습니다.
eddieroger

@Mulmoth는 물론 대답입니다. 증상이 아닌 문제를 해결합니다. 문제는 데이터베이스 디자인에 있습니다.
HLGEM

1
@HLGEM이 문제는 문제를 지적하지만,보다 일반적인 이해 될 수있다. 이 문제는 매우 표준화 된 데이터베이스에 대해 완전히 합법적입니다.
Zeemee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.