SQL varchar内の特定の部分文字列の出現数をどのようにカウントしますか?


回答:


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を返します。誰かに役立つ場合に備えて、これを修正する別の回答を投稿します。
2015

5
LENはトリミングされた文字列のサイズを返すため、LENの代わりにDATALENGTHを使用する方がよいでしょう。
rodrigocl 2015年

2
DATALENGTH()/ 2も、明らかな文字サイズがないためトリッキーです。文字列の長さを取得する簡単で正確な方法については、stackoverflow.com / a / 11080074/1094048をご覧ください。
pkuderov

@rodrigoclなぜLTRIM次のように文字列をラップしないのSELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))ですか?
Alex Bello 2017年

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', ' ')、ゼロ除算で失敗します。
ロリー

5
ロリーのコメントは役に立ちます。アンドリューの関数でLENをDATALENGTHに置き換えるだけで、望ましい結果が得られることがわかりました。数学の計算方法では、2で除算する必要はないようです。
ガーランド教皇

@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

私はこれと同じ機能を私のレガシーデータベースの多くで使用しています。これは、多くの古くて不適切に設計されたデータベースの多くに役立ちます。時間を大幅に節約し、大きなデータセットでも非常に高速です。
カイメン

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)

アスタリスクを使用する必要がないことに注意してください。2文字の置換で十分です。カウントする文字のインスタンスごとに文字列を1文字ずつ長くしてから、元の長さを引くという考え方です。それは基本的に、奇妙なトリミングの副作用を伴わない元の答えの反対の方法です。


5
「何らかの理由で、REPLACEは最後のコンマを取り除きますが、その直前のスペースも(理由がわからないため)」REPLACEは、最後のコンマとその前のスペースを取り除きません。実際には、そのスペースのために文字列の最後にある空白を無視しているのはLEN関数です。
Imranullah Khan、2015

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)を実行する方が簡単/高速である場合があります。これを行う1つの方法は、区切られたレコードをそのままステージングテーブルにロードすることです。例外的なレコードを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

このコードを使用すると、完全に機能します。2つのパラメーターを受け入れるSQL関数を作成しました。最初のパラメーターは、検索する長い文字列であり、最大1500文字の文字列を受け入れることができます(もちろん、拡張したり、テキストデータ型に変更したりすることもできます) )。そして、2番目のパラメーターは、その出現回数を計算する部分文字列です(長さは最大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 2014

-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呼び出しは、使用定義関数よりもはるかに高速ですか?
ダレルリー

大規模な記録では、はい。確かに、大きな文字列を含む大きなレコードセットでテストする必要があります。SQLで手続き型(ループなど)を回避できる場合は、手続き型のものを記述しないでください
Nick.McDermaid

-3

おそらく、そのようにデータを保存するべきではありません。カンマ区切りのリストをフィールドに格納することは悪い習慣です。ITはクエリに対して非常に非効率的です。これは関連テーブルでなければなりません。


そのことを考えるための+1。誰かがフィールドでカンマ区切りのデータを使用するとき、私は通常それから始めます。
グッファ

6
この質問の目的の一部は、そのような既存のデータを取得して適切に分割することでした。
Orion Adrian

7
私たちの一部は、それが行われたレガシーデータベースを与えられており、それについては何もできません。
eddieroger、2014年

@Mulmoth、もちろんそれは答えです。症状ではなく問題を修正します。問題はデータベースの設計にあります。
HLGEM 2014年

1
@HLGEM質問問題指摘している可能性がありますが、より一般的に理解できます。問題は、非常によく正規化されたデータベースに対して完全に正当です。
Zeemee 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.