Як ви рахуєте кількість входжень певної підрядки у варшарі SQL?


150

У мене стовпець із значеннями, відформатованими як a, b, c, d. Чи є спосіб підрахувати кількість коми в цьому значенні в T-SQL?

Відповіді:


245

Перший спосіб, який спадає на думку, - це зробити це опосередковано, замінивши кому на порожню рядок і порівнявши довжини

Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))

13
Це відповідає на запитання, як написано в тексті, але не так, як написано в заголовку. Щоб він працював для більш ніж одного символу, просто потрібно додати / len (searchterm) навколо речі. Опублікував відповідь, якщо вона комусь корисна.
Ендрю Барретт

Хтось зазначив мені, що це не завжди працює так, як очікувалося. Розглянемо наступне: SELECT LEN ('a, b, c, d,') - LEN (ЗАМІНА ('a, b, c, d,', ',', '')) З причин, які я ще не розумію , пробіл між d і кінцевим стовпцем змушує цього повернути 5 замість 4. Я опублікую ще одну відповідь, яка це виправляє, якщо це комусь стане в нагоді.
бурбалка

5
Можливо, використання DATALENGTH замість LEN було б краще, тому що LEN повертає розмір обрізаного рядка.
rodrigocl

2
DATALENGTH () / 2 також складний через неочевидних розмірів знаків. Подивіться на stackoverflow.com/a/11080074/1094048 для простого і точного способу отримання довжини рядка.
pkuderov

@rodrigocl Чому б не обернути LTRIMнавколо рядка так SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', '')):?
Алекс Белло

67

Швидке розширення відповіді 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

16
Невеликим поліпшенням буде використання DATALENGTH () / 2 замість LEN (). LEN ігнорує будь-який пробільний пробіл, тому dbo.CountOccurancesOfString( 'blah ,', ',')поверне 2 замість 1 і dbo.CountOccurancesOfString( 'hello world', ' ')не зможе ділитися на нуль.
Рорі

5
Коментар Рорі корисний. Я виявив, що міг просто замінити LEN на DATALENGTH у функції Ендрю і отримати бажаний результат. Здається, ділити на 2 не потрібно з тим, як працює математика.
Гарленд Папа

@AndrewBarrett: Що додавати, коли кілька рядків мають однакову довжину?
користувач2284570

2
DATALENGTH()/2також складний через неочевидних розмірів діаграм. Подивіться на stackoverflow.com/a/11080074/1094048 для простого і точного способу.
pkuderov

26

Можна порівняти довжину рядка з тією, з якої видаляються коми:

len(value) - len(replace(value,',',''))

8

Спираючись на рішення Ендрю, ви отримаєте набагато кращі показники, використовуючи непроцедурну функцію табличного значення та 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

Я використовую цю ж функцію у багатьох своїх застарілих базах даних, вона дуже допомагає з багатьма старими та неправильно спроектованими базами даних. Економить багато часу та дуже швидко навіть у великих наборах даних.
Каймен

6

У відповіді @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)

Зауважте, що вам не потрібно використовувати зірочки. Будь-яка двозначна заміна буде робити. Ідея полягає в тому, що ви подовжуєте рядок на один символ для кожного екземпляра символу, який ви рахуєте, а потім віднімаєте довжину оригіналу. Це в основному протилежний метод оригінальної відповіді, який не відповідає дивному побічному ефекту.


5
"Чомусь REPLACE позбавляється остаточної коми, але ТАКОЖ пробіл перед ним (не впевнений, чому)." ЗАМІНА не позбавляється останньої коми і пробілу перед нею, це фактично функція LEN, яка ігнорує пробіл, що виникає в кінці рядка через цей простір.
Імранулла хан

2
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)

це фактично повертає на 1 менше фактичного рахунку
Інтегратор

1

Прийнята відповідь є правильною, розширивши її на використання 2 або більше символів у підрядках:

Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)

1

Якщо ми знаємо, що є обмеження на LEN та простір, чому ми не можемо спочатку замінити пробіл? Тоді ми знаємо, що немає місця для сплутування LEN.

len(replace(@string, ' ', '-')) - len(replace(replace(@string, ' ', '-'), ',', ''))

0
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'

0

Даррел Лі, я думаю, має досить гарну відповідь. Замініть CHARINDEX()на PATINDEX(), і ви також можете зробити слабкий regexпошук по рядку ...

Скажімо, ви використовуєте це для @pattern:

set @pattern='%[-.|!,'+char(9)+']%'

Чому ви, можливо, хочете зробити щось таке божевільне?

Скажімо, ви завантажуєте обмежені текстові рядки в таблицю постановки, де поле, що містить дані, є чимось на зразок varchar (8000) або nvarchar (max) ...

Інколи простіше / швидше зробити ELT (Extract-Load-Transform) з даними, а не ETL (Extract-Transform-Load), і один із способів зробити це - завантаження розділених записів як є в таємну таблицю, особливо якщо ви, можливо, хочете простіший спосіб побачити виняткові записи, а не обробляти їх частиною пакету SSIS ... але це свята війна для іншої нитки.


0

Слід виконати фокус як для пошуку одного символу, так і для кількох пошукових символів:

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
          );

0

Використовуйте цей код, він працює чудово. Я створив функцію sql, яка приймає два параметри. Перший парам - це довга рядок, яку ми хочемо шукати в ній, і вона може приймати довжину рядка до 1500 символів (звичайно, ви можете розширити її або навіть змінити її на тип даних тексту ). І другий параметр - це підряд, який ми хочемо обчислити кількість його появи (його довжина до 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

0

Нарешті я записую цю функцію, яка повинна охоплювати всі можливі ситуації, додаючи до входу префікс 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 ,', ',')

0
Declare @MainStr nvarchar(200)
Declare @SubStr nvarchar(10)
Set @MainStr = 'nikhildfdfdfuzxsznikhilweszxnikhil'
Set @SubStr = 'nikhil'
Select (Len(@MainStr) - Len(REPLACE(@MainStr,@SubStr,'')))/Len(@SubStr)

0

У SQL 2017 або новіших версіях ви можете використовувати це:

declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)

0

цей код 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


0

для SQL Server 2017

declare @hits int = 0;
set @hits = (select count(*) from (select value from STRING_SPLIT('F609,4DFA,8499',',')) a);
select @hits;

-1

Ви можете використовувати наступну збережену процедуру для отримання значень.

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

рядок 4 цієї збереженої процедури @c1відповідає відповіді, яку він вимагає. Яке використання решти коду, враховуючи, що для цього потрібна попередньо існуюча таблиця, викликана table1для роботи, має жорсткий кодовий деліметр і не може бути використана в рядку, як прийнятий відповідь за два місяці до цього?
Nick.McDermaid

-1

Тест 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

У будь-якій таблиці помітного розміру використання процедурної функції набагато неефективніше
Nick.McDermaid

Гарна думка. Чи вбудований Лен дзвінок набагато швидше, ніж функція, визначена використанням?
Даррель Лі

При великому масштабі записів, так. Хоча, щоб бути певним, вам доведеться протестувати на великому наборі записів з великими рядками. Ніколи не пишіть нічого процедурного в SQL, якщо ви можете цього уникнути (тобто циклі)
Nick.McDermaid

-3

Можливо, ви не повинні зберігати дані таким чином. Погана практика коли-небудь зберігати список з обмеженими комами в полі. ІТ дуже неефективний для запитів. Це має бути пов'язана таблиця.


+1 для роздуму над цим. З цього я зазвичай починаю, коли хтось використовує дані, розділені комами у полі.
Guffa

6
Частиною мети цього питання було взяти наявні подібні дані та розділити їх належним чином.
Оріон Адріан

7
Дехто з нас отримує застарілі бази даних, де це було зроблено, і ми нічого не можемо з цим зробити.
eddieroger

@Mulmoth, звичайно, це відповідь. ви вирішите проблему не симптом. Проблема полягає в дизайні бази даних.
HLGEM

1
@HLGEM Питання може вказувати на проблему, але його можна зрозуміти загальніше. Питання цілком законне для дуже добре нормалізованих баз даних.
Zeemee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.