web-dev-qa-db-fra.com

Comment comptez-vous le nombre d'occurrences d'une certaine sous-chaîne dans un varchar SQL?

J'ai une colonne qui a des valeurs formatées comme a, b, c, d. Existe-t-il un moyen de compter le nombre de virgules dans cette valeur dans T-SQL?

121
Orion Adrian

La première façon de penser est de le faire indirectement en remplaçant la virgule par une chaîne vide et en comparant les longueurs.

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

Extension rapide de la réponse de cmsjr qui fonctionne pour les chaînes comportant plus de caractères.

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
62
Andrew Barrett

Vous pouvez comparer la longueur de la chaîne avec celle où les virgules sont supprimées:

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

La réponse de @csmjr pose problème dans certains cas.

Sa réponse était de faire ceci:

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

Cela fonctionne dans la plupart des scénarios, cependant, essayez d’exécuter ceci:

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

Pour une raison quelconque, REPLACE supprime la virgule finale mais AUSSI l’espace situé juste avant (je ne sais pas pourquoi). Cela donne une valeur renvoyée de 5 alors que vous en attendiez 4. Voici une autre façon de procéder qui fonctionnera même dans ce scénario spécial:

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

Notez que vous n'avez pas besoin d'utiliser d'astérisques. Tout remplacement de deux caractères fera l'affaire. L'idée est de rallonger la chaîne d'un caractère pour chaque occurrence du caractère que vous comptez, puis de soustraire la longueur de l'original. C'est fondamentalement la méthode opposée de la réponse originale qui ne vient pas avec l'effet de coupe étrange.

4
bubbleking

En vous appuyant sur la solution de @ Andrew, vous obtiendrez de bien meilleures performances en utilisant une fonction de valeur table non procédurale et 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
3
Russell Fox
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)
2
NIKHIL THAKUR

La réponse acceptée est correcte, L’étendre pour utiliser 2 caractères ou plus dans la sous-chaîne:

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

Je pense que Darrel Lee a une très bonne réponse. Remplacez CHARINDEX() par PATINDEX(), et vous pourrez également effectuer une recherche regex faible le long d'une chaîne ...

Comme, disons que vous utilisez ceci pour @pattern:

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

Pourquoi voudriez-vous peut-être faire quelque chose de fou comme ça?

Supposons que vous chargiez des chaînes de texte délimitées dans une table intermédiaire, où le champ contenant les données ressemble à varchar (8000) ou nvarchar (max) ... 

Parfois, il est plus facile/plus rapide de faire ELT (Extract-Load-Transform) avec des données plutôt que ETL (Extract-Transform-Load), et une façon de faire est de charger les enregistrements délimités tels quels dans une table intermédiaire, en particulier si vous voudrez peut-être un moyen plus simple de voir les enregistrements exceptionnels plutôt que de les traiter dans le cadre d'un paquet SSIS ... mais c'est une guerre sainte pour un fil différent.

0
user1390375

Utilisez ce code, il fonctionne parfaitement ... J'ai créé une fonction SQL qui accepte deux paramètres, le premier paramètre est la longue chaîne que nous voulons rechercher, et il peut accepter une longueur maximale de 1500 caractères (de Bien sûr, vous pouvez l'étendre ou même le changer en type de données texte) . Et le deuxième paramètre est la sous-chaîne que nous voulons calculer le nombre de son occurrence (sa longueur est jusqu'à 200 caractères, bien sûr, vous pouvez le changer pour de quoi avez-vous besoin? et la sortie est un entier, représente le nombre de fréquence ..... profitez-en.


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
One Day

Ce qui suit devrait faire l'affaire pour les recherches sur un ou plusieurs caractères:

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;

La fonction peut être simplifiée un peu en utilisant une table de nombres (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
cmfox1970

J'écris enfin cette fonction qui devrait couvrir toutes les situations possibles, en ajoutant un préfixe et un suffixe de caractère à l'entrée. ce caractère est considéré différent de l'un des caractères contenus dans le paramètre de recherche, il ne peut donc pas affecter le résultat.

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 ,', ',')
0
Arden Inside
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
Shiva

En SQL 2017 ou supérieur, vous pouvez utiliser ceci:

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

ce code T-SQL trouve et imprime toutes les occurrences du motif @p dans la phrase @s. vous pouvez faire n'importe quel traitement sur la phrase après.

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

le résultat est: 1 6 13 20

0
Hasan Zafari

Si nous savons qu’il existe une limitation de LEN et d’espace, pourquoi ne pouvons-nous pas d'abord remplacer l’espace?.

len(replace(@string, ' ', '-')) - len(replace(replace(@string, ' ', '-'), ',', ''))
0
MartinC
Declare @MainStr nvarchar(200)
Declare @SubStr nvarchar(10)
Set @MainStr = 'nikhildfdfdfuzxsznikhilweszxnikhil'
Set @SubStr = 'nikhil'
Select (Len(@MainStr) - Len(REPLACE(@MainStr,@SubStr,'')))/Len(@SubStr)
0
NIKHIL THAKUR