Ho una colonna con valori formattati come a, b, c, d. C'è un modo per contare il numero di virgole in quel valore in T-SQL?
Ho una colonna con valori formattati come a, b, c, d. C'è un modo per contare il numero di virgole in quel valore in T-SQL?
Risposte:
Il primo modo che viene in mente è farlo indirettamente sostituendo la virgola con una stringa vuota e confrontando le lunghezze
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
LTRIMgiro la stringa come segue: SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))?
Rapida estensione della risposta di cmsjr che funziona per stringhe con più di più caratteri.
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 ,', ',')restituirà 2 invece di 1 e dbo.CountOccurancesOfString( 'hello world', ' ')non riuscirà con divisione per zero.
DATALENGTH()/2è anche complicato a causa di dimensioni dei caratteri non ovvie. Guarda stackoverflow.com/a/11080074/1094048 per un modo semplice e preciso.
Basandoti sulla soluzione di @ Andrew, otterrai prestazioni molto migliori utilizzando una funzione a valori di tabella non procedurale e 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 risposta di @csmjr ha dei problemi in alcuni casi.
La sua risposta era di fare questo:
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
Funziona nella maggior parte degli scenari, tuttavia, prova a eseguire questo:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))
Per qualche motivo, REPLACE elimina la virgola finale ma ANCHE lo spazio appena prima di esso (non so perché). Ciò si traduce in un valore restituito di 5 quando ti aspetteresti 4. Ecco un altro modo per farlo che funzionerà anche in questo scenario speciale:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)
Nota che non è necessario utilizzare gli asterischi. Qualsiasi sostituzione a due caratteri farà. L'idea è di allungare la stringa di un carattere per ogni istanza del personaggio che stai contando, quindi sottrarre la lunghezza dell'originale. È sostanzialmente il metodo opposto della risposta originale che non ha lo strano effetto collaterale di rifilatura.
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 risposta accettata è corretta, estendendola per utilizzare 2 o più caratteri nella sottostringa:
Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)
Darrel Lee Penso che abbia una risposta abbastanza buona. Sostituiscilo CHARINDEX()con PATINDEX()e puoi fare anche qualche regexricerca debole lungo una stringa ...
Ad esempio, supponi di usarlo per @pattern:
set @pattern='%[-.|!,'+char(9)+']%'
Perché vorresti forse fare qualcosa di folle come questo?
Supponi di caricare stringhe di testo delimitate in una tabella di gestione temporanea, in cui il campo contenente i dati è simile a varchar (8000) o nvarchar (max) ...
A volte è più facile / veloce eseguire ELT (Estrai-Carica-Trasforma) con dati anziché ETL (Estrai-Trasforma-Carica), e un modo per farlo è caricare i record delimitati così com'è in una tabella di gestione temporanea, specialmente se potresti voler un modo più semplice di vedere i record eccezionali piuttosto che gestirli come parte di un pacchetto SSIS ... ma questa è una guerra santa per un thread diverso.
Quanto segue dovrebbe fare il trucco sia per le ricerche di caratteri singoli che multipli:
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 funzione può essere semplificata un po 'usando una tabella di numeri (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
);
Usa questo codice, funziona perfettamente. Ho creato una funzione sql che accetta due parametri, il primo parametro è la stringa lunga che vogliamo cercare in essa e può accettare una lunghezza della stringa fino a 1500 caratteri (ovviamente puoi estenderla o persino cambiarla in tipo di dati di testo ). E il secondo parametro è la sottostringa che vogliamo calcolare il numero della sua occorrenza (la sua lunghezza è fino a 200 caratteri, ovviamente puoi cambiarla in base alle tue esigenze). e l'output è un numero intero, rappresenta il numero di frequenza ..... divertitevi.
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 scrivo questa funzione che dovrebbe coprire tutte le possibili situazioni, aggiungendo un prefisso char e un suffisso all'input. questo carattere viene valutato come diverso da qualsiasi carattere inserito nel parametro di ricerca, quindi non può influenzare il risultato.
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)
In SQL 2017 o versioni successive, puoi utilizzare questo:
declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)
questo codice T-SQL trova e stampa tutte le occorrenze del pattern @p nella frase @s. successivamente è possibile eseguire qualsiasi elaborazione sulla frase.
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
il risultato è: 1 6 13 20
per SQL Server 2017
declare @hits int = 0;
set @hits = (select count(*) from (select value from STRING_SPLIT('F609,4DFA,8499',',')) a);
select @hits;
È possibile utilizzare la seguente procedura memorizzata per recuperare valori.
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
@c1la risposta richiesta. A che cosa serve il resto del codice, considerando che ha bisogno di una tabella preesistente chiamata table1per funzionare, ha un delimitatore codificato e non può essere utilizzato in linea come la risposta accettata di due mesi prima?
Il test Sostituisci / Len è carino, ma probabilmente molto inefficiente (soprattutto in termini di memoria). Una semplice funzione con un ciclo farà il lavoro.
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
Forse non dovresti archiviare i dati in questo modo. È una cattiva pratica archiviare mai un elenco delimitato da virgole in un campo. L'IT è molto inefficiente per le query. Questa dovrebbe essere una tabella correlata.