Mam kolumnę z wartościami w formacie a, b, c, d. Czy istnieje sposób, aby policzyć liczbę przecinków w tej wartości w T-SQL?
Mam kolumnę z wartościami w formacie a, b, c, d. Czy istnieje sposób, aby policzyć liczbę przecinków w tej wartości w T-SQL?
Odpowiedzi:
Pierwszym sposobem, który przychodzi na myśl, jest zrobienie tego pośrednio, zastępując przecinek pustym ciągiem i porównując długości
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
LTRIM
wokół napisu w następujący sposób: SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))
?
Szybkie rozszerzenie odpowiedzi cmsjr, które działa w przypadku łańcuchów zawierających więcej niż więcej znaków.
CREATE FUNCTION dbo.CountOccurrencesOfString
(
@searchString nvarchar(max),
@searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
return (LEN(@searchString)-LEN(REPLACE(@searchString,@searchTerm,'')))/LEN(@searchTerm)
END
Stosowanie:
SELECT * FROM MyTable
where dbo.CountOccurrencesOfString(MyColumn, 'MyString') = 1
dbo.CountOccurancesOfString( 'blah ,', ',')
zwróci 2 zamiast 1 i dbo.CountOccurancesOfString( 'hello world', ' ')
zakończy się niepowodzeniem z dzieleniem przez zero.
DATALENGTH()/2
jest również trudna z powodu nieoczywistych rozmiarów znaków. Zajrzyj na stackoverflow.com/a/11080074/1094048, aby uzyskać prosty i dokładny sposób.
Opierając się na rozwiązaniu @ Andrew, uzyskasz znacznie lepszą wydajność przy użyciu nieproceduralnej funkcji wartościującej tabelę i 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
Odpowiedź udzielona przez @csmjr zawiera w niektórych przypadkach problem.
Jego odpowiedź brzmiała:
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
Działa to w większości scenariuszy, jednak spróbuj uruchomić to:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))
Z jakiegoś powodu polecenie REPLACE usuwa ostatni przecinek, ale RÓWNIEŻ spację tuż przed nim (nie wiem dlaczego). Powoduje to zwrócenie wartości 5, gdy można się spodziewać 4. Oto inny sposób na zrobienie tego, który zadziała nawet w tym specjalnym scenariuszu:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)
Pamiętaj, że nie musisz używać gwiazdek. Każde zastąpienie dwóch znaków wystarczy. Chodzi o to, że wydłużasz ciąg o jeden znak dla każdego wystąpienia liczonego znaku, a następnie odejmujesz długość oryginału. Jest to w zasadzie metoda odwrotna do oryginalnej odpowiedzi, która nie wiąże się z dziwnym efektem ubocznym przycinania.
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)
Zaakceptowana odpowiedź jest poprawna, rozszerzając ją tak, aby używała 2 lub więcej znaków w podłańcuchu:
Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)
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'
Myślę, że Darrel Lee ma całkiem dobrą odpowiedź. Wymień CHARINDEX()
się PATINDEX()
, i można to zrobić trochę słaby regex
poszukiwania wzdłuż łańcucha, zbyt ...
Powiedz, że używasz tego do @pattern
:
set @pattern='%[-.|!,'+char(9)+']%'
Dlaczego miałbyś chcieć zrobić coś tak szalonego?
Powiedzmy, że ładujesz rozdzielone ciągi tekstowe do tabeli pomostowej, w której pole przechowujące dane jest czymś w rodzaju varchar (8000) lub nvarchar (max) ...
Czasami łatwiej / szybciej jest wykonać ELT (wyodrębnij-załaduj-transformację) z danymi zamiast ETL (wyodrębnij-transformuj-załaduj), a jednym ze sposobów na to jest załadowanie rozdzielonych rekordów w postaci, w jakiej są, do tabeli pomostowej, zwłaszcza jeśli możesz chcieć prostszego sposobu, aby zobaczyć wyjątkowe zapisy, zamiast traktować je jako część pakietu SSIS ... ale to święta wojna na inny wątek.
Poniższe działania powinny załatwić sprawę zarówno w przypadku wyszukiwania pojedynczego znaku, jak i wyszukiwania wielu znaków:
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;
Funkcję można nieco uprościć za pomocą tabeli liczb (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
);
Użyj tego kodu, działa idealnie. Stworzyłem funkcję sql akceptującą dwa parametry, pierwszym parametrem jest długi ciąg, który chcemy w nim przeszukać i może on przyjmować długość ciągu do 1500 znaków (oczywiście możesz go przedłużyć lub nawet zmienić na typ danych tekstowych ). A drugim parametrem jest podłańcuch, który chcemy obliczyć liczbę jego wystąpień (jego długość to maksymalnie 200 znaków, oczywiście możesz go zmienić na taką, jakiej potrzebujesz). a wyjście jest liczbą całkowitą, reprezentuje liczbę częstotliwości… ciesz się tym.
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
W końcu piszę tę funkcję, która powinna obejmować wszystkie możliwe sytuacje, dodając prefiks i sufiks znaku do wejścia. ten znak jest oceniany jako inny niż którykolwiek ze znaków kontekstowych w parametrze wyszukiwania, więc nie może wpływać na wynik.
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
stosowanie
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)
W programie SQL 2017 lub nowszym możesz użyć tego:
declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)
ten kod T-SQL wyszukuje i wypisuje wszystkie wystąpienia wzorca @p w zdaniu @s. możesz później przetworzyć wyrok.
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
wynik to: 1 6 13 20
dla programu SQL Server 2017
declare @hits int = 0;
set @hits = (select count(*) from (select value from STRING_SPLIT('F609,4DFA,8499',',')) a);
select @hits;
Aby pobrać wartości, można użyć następującej procedury składowanej.
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
odpowiedź, której on potrzebuje. Jaki pożytek jest z pozostałej części kodu, biorąc pod uwagę, że wymaga on wcześniej istniejącej tabeli wywoływanej table1
do pracy, ma zakodowany na stałe ogranicznik i nie może być używany w tekście, jak zaakceptowana odpowiedź sprzed dwóch miesięcy?
Test Replace / Len jest uroczy, ale prawdopodobnie bardzo nieefektywny (szczególnie pod względem pamięci). Wystarczy prosta funkcja z pętlą.
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
Być może nie powinieneś przechowywać danych w ten sposób. Przechowywanie listy rozdzielanej przecinkami w polu jest złą praktyką. IT jest bardzo nieefektywne do wykonywania zapytań. To powinna być powiązana tabela.