Eu tenho uma coluna que possui valores formatados como a, b, c, d. Existe uma maneira de contar o número de vírgulas nesse valor no T-SQL?
Eu tenho uma coluna que possui valores formatados como a, b, c, d. Existe uma maneira de contar o número de vírgulas nesse valor no T-SQL?
Respostas:
A primeira maneira que vem à mente é fazê-lo indiretamente, substituindo a vírgula por uma sequência vazia e comparando os comprimentos
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
LTRIM
ao redor da string da seguinte maneira SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))
:?
Extensão rápida da resposta do cmsjr que funciona para cadeias de caracteres com mais de mais caracteres.
CREATE FUNCTION dbo.CountOccurrencesOfString
(
@searchString nvarchar(max),
@searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
return (LEN(@searchString)-LEN(REPLACE(@searchString,@searchTerm,'')))/LEN(@searchTerm)
END
Uso:
SELECT * FROM MyTable
where dbo.CountOccurrencesOfString(MyColumn, 'MyString') = 1
dbo.CountOccurancesOfString( 'blah ,', ',')
, retornará 2 em vez de 1 e dbo.CountOccurancesOfString( 'hello world', ' ')
falhará com a divisão por zero.
DATALENGTH()/2
também é complicado por causa dos tamanhos de caracteres não óbvios. Veja stackoverflow.com/a/11080074/1094048 para obter uma maneira simples e precisa.
Com base na solução da @ Andrew, você obterá um desempenho muito melhor usando uma função com valor de tabela não processual e o 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
A resposta de @csmjr tem um problema em alguns casos.
Sua resposta foi fazer o seguinte:
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
Isso funciona na maioria dos cenários, no entanto, tente executar o seguinte:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))
Por alguma razão, REPLACE se livra da vírgula final, mas TAMBÉM o espaço logo antes (não sei por que). Isso resulta em um valor retornado de 5 quando você esperaria 4. Aqui está outra maneira de fazer isso que funcionará mesmo neste cenário especial:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)
Observe que você não precisa usar asteriscos. Qualquer substituição de dois caracteres serve. A idéia é que você estenda a string em um caractere para cada instância do caractere que está contando e subtraia a extensão do original. É basicamente o método oposto da resposta original que não vem com o estranho efeito colateral de corte.
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)
A resposta aceita está correta, estendendo-a para usar 2 ou mais caracteres na substring:
Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)
Darrel Lee, acho que tem uma resposta muito boa. Substitua CHARINDEX()
por PATINDEX()
e você também pode fazer uma regex
pesquisa fraca ao longo de uma string ...
Como, digamos que você use isso para @pattern
:
set @pattern='%[-.|!,'+char(9)+']%'
Por que você talvez queira fazer algo louco como esse?
Digamos que você esteja carregando seqüências de texto delimitadas em uma tabela intermediária, em que o campo que contém os dados é algo como um varchar (8000) ou nvarchar (max) ...
Às vezes, é mais fácil / rápido executar o ELT (Extract-Load-Transform) com dados, em vez de ETL (Extract-Transform-Load), e uma maneira de fazer isso é carregar os registros delimitados como estão em uma tabela de preparação, especialmente se você pode querer uma maneira mais simples de ver os registros excepcionais, em vez de lidar com eles como parte de um pacote SSIS ... mas essa é uma guerra santa por um segmento diferente.
A seguir, você deve executar o truque para pesquisas de caracteres únicos e vários caracteres:
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;
A função pode ser simplificada um pouco usando uma tabela de números (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
);
Use esse código, ele está funcionando perfeitamente. Eu criei uma função sql que aceita dois parâmetros, o primeiro parâmetro é a string longa que queremos pesquisar e pode aceitar o comprimento da string com até 1500 caracteres (é claro que você pode estendê-la ou mesmo alterá-la para tipo de dados de texto ) E o segundo parâmetro é a substring que queremos calcular o número de ocorrências (seu tamanho é de até 200 caracteres, é claro que você pode alterá-lo conforme sua necessidade). e a saída é um número inteiro, representa o número de frequência ..... aproveite.
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
Finalmente, escrevo esta função que deve cobrir todas as situações possíveis, adicionando um prefixo e um sufixo de caracteres à entrada. esse caractere é avaliado como diferente de qualquer caractere contido no parâmetro de pesquisa, portanto, não pode afetar o resultado.
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
uso
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)
No SQL 2017 ou superior, você pode usar este:
declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)
esse código T-SQL localiza e imprime todas as ocorrências do padrão @p na frase @s. você pode executar qualquer processamento da sentença posteriormente.
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
o resultado é: 1 6 13 20
para SQL Server 2017
declare @hits int = 0;
set @hits = (select count(*) from (select value from STRING_SPLIT('F609,4DFA,8499',',')) a);
select @hits;
Você pode usar o seguinte procedimento armazenado para buscar valores.
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
a resposta que ele exige. Qual é a utilidade do restante do código, considerando que ele precisa de uma tabela pré-existente chamada table1
para funcionar, possui um delímetro codificado e não pode ser usado em linha como a resposta aceita dos dois meses anteriores?
O teste Substituir / Len é bonito, mas provavelmente muito ineficiente (especialmente em termos de memória). Uma função simples com um loop fará o trabalho.
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
Talvez você não deva armazenar dados dessa maneira. É uma prática ruim armazenar sempre uma lista delimitada por vírgulas em um campo. A TI é muito ineficiente para consultas. Essa deve ser uma tabela relacionada.