У меня есть столбец, значения которого отформатированы как a, b, c, d. Есть ли способ подсчитать количество запятых в этом значении в T-SQL?
У меня есть столбец, значения которого отформатированы как a, b, c, d. Есть ли способ подсчитать количество запятых в этом значении в T-SQL?
Ответы:
Первый способ, который приходит на ум, - сделать это косвенно, заменив запятую пустой строкой и сравнив длину
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
LTRIMвокруг строки следующим образом : SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))?
Быстрое расширение ответа cmsjr, которое работает со строками из более чем одного символа.
CREATE FUNCTION dbo.CountOccurrencesOfString
(
@searchString nvarchar(max),
@searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
return (LEN(@searchString)-LEN(REPLACE(@searchString,@searchTerm,'')))/LEN(@searchTerm)
END
Использование:
SELECT * FROM MyTable
where dbo.CountOccurrencesOfString(MyColumn, 'MyString') = 1
dbo.CountOccurancesOfString( 'blah ,', ',')вернет 2 вместо 1 и dbo.CountOccurancesOfString( 'hello world', ' ')потерпит неудачу с делением на ноль.
DATALENGTH()/2также сложно из-за неочевидных размеров символов. Посмотрите на stackoverflow.com/a/11080074/1094048 для простого и точного способа.
Основываясь на решении @ Andrew, вы получите гораздо лучшую производительность, используя непроцедурную табличную функцию и 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
Ответ @csmjr имеет проблему в некоторых случаях.
Его ответом было сделать это:
Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))
Это работает в большинстве сценариев, однако попробуйте запустить это:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))
По какой-то причине REPLACE избавляется от последней запятой, но ТАКЖЕ и непосредственно перед ней (не знаю почему). Это приводит к возвращаемому значению 5, когда вы ожидаете 4. Вот другой способ сделать это, который будет работать даже в этом специальном сценарии:
DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)
Обратите внимание, что вам не нужно использовать звездочки. Подойдет любая двухсимвольная замена. Идея состоит в том, что вы удлиняете строку на один символ для каждого экземпляра счетного символа, а затем вычитаете длину оригинала. Это в основном противоположный метод первоначального ответа, который не сопровождается странным побочным эффектом обрезки.
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)
Принятый ответ является правильным, расширяя его, чтобы использовать 2 или более символа в подстроке:
Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)
Даррел Ли, я думаю, имеет довольно хороший ответ. Замените CHARINDEX()на PATINDEX(), и вы также можете выполнить слабый regexпоиск по строке ...
Как, скажем, вы используете это для @pattern:
set @pattern='%[-.|!,'+char(9)+']%'
Почему вы хотите сделать что-то сумасшедшее?
Допустим, вы загружаете текстовые строки с разделителями в промежуточную таблицу, где поле, содержащее данные, является чем-то вроде varchar (8000) или nvarchar (max) ...
Иногда проще / быстрее выполнить ELT (Extract-Load-Transform) с данными, а не ETL (Extract-Transform-Load), и один из способов сделать это - загрузить записи с разделителями как есть в промежуточную таблицу, особенно если Вы можете захотеть более простой способ увидеть исключительные записи, чем рассматривать их как часть пакета служб SSIS ... но это священная война для другого потока.
Следующее должно сделать трюк как для поиска одного символа, так и для поиска нескольких символов:
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;
Функция может быть немного упрощена с помощью таблицы чисел (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
);
Используйте этот код, он работает отлично. Я создал функцию sql, которая принимает два параметра, первый параметр - это длинная строка, которую мы хотим найти в ней, и она может принимать длину строки до 1500 символов (конечно, вы можете расширить ее или даже изменить на тип данных text). ). А вторым параметром является подстрока, по которой мы хотим вычислить номер ее вхождения (его длина составляет до 200 символов, конечно, вы можете изменить его на то, что вам нужно). и вывод является целым числом, представляющим число частот ..... наслаждайтесь этим.
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
Наконец, я пишу эту функцию, которая должна охватывать все возможные ситуации, добавляя префикс char и суффикс к входу. этот символ оценивается как отличный от любого символа, указанного в параметре поиска, поэтому он не может повлиять на результат.
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
использование
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)
В SQL 2017 или выше вы можете использовать это:
declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)
этот код T-SQL находит и печатает все вхождения шаблона @p в предложении @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
результат: 1 6 13 20
для SQL Server 2017
declare @hits int = 0;
set @hits = (select count(*) from (select value from STRING_SPLIT('F609,4DFA,8499',',')) a);
select @hits;
Вы можете использовать следующую хранимую процедуру для получения значений.
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ответ, который ему требуется. Какая польза от остальной части кода, учитывая, что ему нужна уже существующая таблица, призванная table1работать, имеет жестко закодированный разделитель и ее нельзя использовать в строке, как принятый ответ за два месяца до этого?
Тест Replace / Len хорош, но, вероятно, очень неэффективен (особенно с точки зрения памяти). Простая функция с циклом сделает работу.
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
Возможно, вам не следует хранить данные таким образом. Не рекомендуется хранить список с разделителями-запятыми в поле. Это очень неэффективно для запросов. Это должна быть связанная таблица.