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?
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?
Réponses:
La première façon qui me vient à l'esprit 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, ',', ''))
LTRIM
autour de la chaîne comme suit: SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))
?
Extension rapide de la réponse de cmsjr qui fonctionne pour les chaînes de plus que 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
dbo.CountOccurancesOfString( 'blah ,', ',')
retournera donc 2 au lieu de 1 et dbo.CountOccurancesOfString( 'hello world', ' ')
échouera avec une division par zéro.
DATALENGTH()/2
est également délicat à cause des tailles de caractères non évidentes. Regardez stackoverflow.com/a/11080074/1094048 pour une manière simple et précise.
En vous basant sur la solution de @ Andrew, vous obtiendrez de bien meilleures performances en utilisant une fonction 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
La réponse de @csmjr pose un problème dans certains cas.
Sa réponse a été 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 juste avant (je ne sais pas pourquoi). Cela se traduit par une valeur renvoyée de 5 lorsque vous vous attendez à 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 d'allonger la chaîne d'un caractère pour chaque instance 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'étrange effet secondaire de coupe.
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)
La réponse acceptée est correcte, l'étendant 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)
Je pense que Darrel Lee a une assez bonne réponse. Remplacez CHARINDEX()
par PATINDEX()
, et vous pouvez également effectuer une regex
recherche 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 à un varchar (8000) ou un 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 le 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 package SSIS ... mais c'est une guerre sainte pour un thread différent.
Ce qui suit devrait faire l'affaire pour les recherches à un seul caractère et à 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 un peu simplifiée à l'aide d'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
);
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 elle peut accepter une longueur de chaîne allant jusqu'à 1500 caractères (bien sûr, vous pouvez l'étendre ou même la changer en type de données texte ). Et le deuxième paramètre est la sous-chaîne dont nous voulons calculer le numéro de son occurrence (sa longueur peut aller jusqu'à 200 caractères, bien sûr, vous pouvez la modifier selon vos besoins). 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
J'écris enfin cette fonction qui devrait couvrir toutes les situations possibles, en ajoutant un préfixe char et un suffixe à l'entrée. ce caractère est évalué comme étant différent de tout caractère contenu 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 ,', ',')
Declare @MainStr nvarchar(200)
Declare @SubStr nvarchar(10)
Set @MainStr = 'nikhildfdfdfuzxsznikhilweszxnikhil'
Set @SubStr = 'nikhil'
Select (Len(@MainStr) - Len(REPLACE(@MainStr,@SubStr,'')))/Len(@SubStr)
Dans SQL 2017 ou version ultérieure, vous pouvez utiliser ceci:
declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)
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 par la suite.
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
pour SQL Server 2017
declare @hits int = 0;
set @hits = (select count(*) from (select value from STRING_SPLIT('F609,4DFA,8499',',')) a);
select @hits;
Vous pouvez utiliser la procédure stockée suivante pour récupérer des valeurs.
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
@c1
la réponse qu'il requiert. À quoi sert le reste du code, étant donné qu'il a besoin d'une table préexistante appelée table1
pour fonctionner, a un délimètre codé en dur et ne peut pas être utilisé en ligne comme la réponse acceptée de deux mois auparavant?
Le test Replace / Len est mignon, mais probablement très inefficace (surtout en termes de mémoire). Une simple fonction avec une boucle fera l'affaire.
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
Vous ne devriez peut-être pas stocker les données de cette façon. Il est déconseillé de stocker une liste délimitée par des virgules dans un champ. L'IT est très inefficace pour les requêtes. Cela devrait être une table liée.