Tengo una columna que tiene valores formateados como a, b, c, d. ¿Hay alguna manera de contar la cantidad de comas en ese valor en T-SQL?
Tengo una columna que tiene valores formateados como a, b, c, d. ¿Hay alguna manera de contar la cantidad de comas en ese valor en T-SQL?
Respuestas:
La primera forma que viene a la mente es hacerlo indirectamente reemplazando la coma con una cadena vacía y comparando las longitudes
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
LTRIM
cadena alrededor de la siguiente manera SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))
:?
Extensión rápida de la respuesta de cmsjr que funciona para cadenas de más que más 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 ,', ',')
lo que devolverá 2 en lugar de 1 y dbo.CountOccurancesOfString( 'hello world', ' ')
fallará al dividir por cero.
DATALENGTH()/2
También es complicado debido a los tamaños de caracteres no obvios. Mire stackoverflow.com/a/11080074/1094048 para obtener una forma simple y precisa.
Basándose en la solución de @ Andrew, obtendrá un rendimiento mucho mejor utilizando una función de tabla de valores no procesal y APLICACIÓN CRUZADA:
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 respuesta de @csmjr tiene un problema en algunos casos.
Su respuesta fue hacer esto:
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
Esto funciona en la mayoría de los escenarios, sin embargo, intente ejecutar esto:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))
Por alguna razón, REPLACE elimina la coma final, pero TAMBIÉN el espacio justo antes (no estoy seguro de por qué). Esto da como resultado un valor devuelto de 5 cuando esperarías 4. Aquí hay otra forma de hacerlo que funcionará incluso en este escenario especial:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)
Tenga en cuenta que no necesita usar asteriscos. Cualquier reemplazo de dos caracteres servirá. La idea es que alargue la cadena en un carácter para cada instancia del carácter que está contando, luego reste la longitud del original. Básicamente es el método opuesto de la respuesta original que no viene con el extraño efecto secundario de recorte.
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 respuesta aceptada es correcta, extendiéndola para usar 2 o más caracteres en la subcadena:
Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)
Darrel Lee creo que tiene una respuesta bastante buena. Reemplace CHARINDEX()
con PATINDEX()
, y también puede hacer una regex
búsqueda débil a lo largo de una cadena ...
Como, digamos que usa esto para @pattern
:
set @pattern='%[-.|!,'+char(9)+']%'
¿Por qué querrías hacer algo loco como esto?
Digamos que está cargando cadenas de texto delimitadas en una tabla de etapas, donde el campo que contiene los datos es algo así como un varchar (8000) o nvarchar (max) ...
A veces es más fácil / rápido hacer ELT (Extract-Load-Transform) con datos en lugar de ETL (Extract-Transform-Load), y una forma de hacerlo es cargar los registros delimitados tal como están en una tabla de etapas, especialmente si Es posible que desee una forma más simple de ver los registros excepcionales en lugar de tratarlos como parte de un paquete SSIS ... pero esa es una guerra santa para un hilo diferente.
Lo siguiente debería hacer el truco para las búsquedas de caracteres únicos y múltiples:
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 función se puede simplificar un poco usando una tabla 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 este código, está funcionando perfectamente. He creado una función sql que acepta dos parámetros, el primer parámetro es la cadena larga que queremos buscar en él, y puede aceptar una longitud de cadena de hasta 1500 caracteres (por supuesto, puede extenderlo o incluso cambiarlo al tipo de datos de texto ) Y el segundo parámetro es la subcadena que queremos calcular el número de su ocurrencia (su longitud es de hasta 200 caracteres, por supuesto, puede cambiarlo a lo que necesite). y la salida es un número entero, representa el número de frecuencia ..... disfrútalo.
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 escribo esta función que debería cubrir todas las situaciones posibles, agregando un prefijo y sufijo char a la entrada. Este carácter se evalúa como diferente de cualquiera de los caracteres contenidos en el parámetro de búsqueda, por lo que no puede afectar el 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)
En SQL 2017 o superior, puede usar esto:
declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)
Este código T-SQL encuentra e imprime todas las apariciones del patrón @p en la oración @s. Puedes hacer cualquier procesamiento en la oración despué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
el resultado es: 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;
Puede usar el siguiente procedimiento almacenado para obtener 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
la respuesta que requiere. ¿De qué sirve el resto del código, considerando que necesita una tabla preexistente llamada table1
al trabajo, tiene un delimitador codificado y no puede usarse en línea como la respuesta aceptada de dos meses antes?
La prueba Reemplazar / Len es linda, pero probablemente muy ineficiente (especialmente en términos de memoria). Una función simple con un bucle hará el trabajo.
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
Quizás no debería almacenar datos de esa manera. Es una mala práctica almacenar una lista delimitada por comas en un campo. Es muy ineficiente para las consultas. Esta debería ser una tabla relacionada.