2009-04-10 3 views

ответ

171

Первый способ, который приходит на ум, чтобы сделать это косвенно, заменив запятую с пустой строкой и сравнения длин

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

Это отвечает на вопрос, как написано в тексте, но не как написано в названии. Чтобы сделать работу более чем одним персонажем, просто нужно добавить/len (searchterm) вокруг вещи. Написал ответ в случае, если это полезно для кого-то. –

+0

Кто-то указал мне, что это не всегда работает так, как ожидалось. Рассмотрим следующее: SELECT LEN ('a, b, c, d,') - LEN (ЗАМЕНИТЬ ('a, b, c, d,', ',', '')) По причинам, Пока еще не понятно, пространство между d и последним столбцом заставляет это возвращать 5 вместо 4. Я отправлю еще один ответ, который исправляет это, в случае, если это полезно кому угодно. – bubbleking

+2

Возможно, использование DATALENGTH вместо LEN было бы лучше, потому что LEN возвращает размер обрезанной строки. – rodrigocl

12

Вы можете сравнить длину строки с одной, где запятые удаляются :

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

Возможно, вы не должны хранить данные таким образом. Плохая практика всегда хранить список с разделителями-запятыми в поле. Это очень неэффективно для запросов. Это должна быть связанная таблица.

+0

+1 за это. Это то, что я обычно начинаю с того, когда кто-то использует данные, разделенные запятыми, в поле. – Guffa

+6

Часть цели этого вопроса заключалась в том, чтобы использовать существующие данные и разделить их соответствующим образом. –

+6

Некоторые из нас получают устаревшие базы данных, где это было сделано, и мы ничего не можем с этим поделать. – eddieroger

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

Быстрое расширение ответа cmsjr, которое работает для строк более чем символа.

CREATE FUNCTION dbo.CountOccurancesOfString 
(
    @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.CountOccurancesOfString(MyColumn, 'MyString') = 1 
+13

Небольшое улучшение было бы использовать DATALENGTH()/2 вместо LEN(). LEN игнорирует любые конечные пробелы, поэтому 'dbo.CountOccurancesOfString ('blah,', ',')' вернет 2 вместо 1 и 'dbo.CountOccurancesOfString ('hello world', '')' будет терпеть неудачу с делением на ноль. – Rory

+4

Комментарий Рори очень полезен. Я обнаружил, что могу просто заменить LEN DATALENGTH в функции Эндрю и получить желаемый результат. Кажется, что разделение на 2 не обязательно с помощью математики. –

+0

@AndrewBarrett: Что добавить, когда несколько строк имеют одинаковую длину? – user2284570

-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 [email protected]; 
    while (@c<[email protected]) 
    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 @[email protected]+1 
    end 
+0

строка 4 этой хранимой процедуры устанавливает '@ c1' ответ, который он требует. Какая польза от остальной части кода, учитывая, что для работы требуется предварительно существующая таблица, называемая «table1», имеет жесткий кодированный делитель и не может использоваться как встроенный, как принятый ответ за два месяца до этого? –

1
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

Заменить/испытание 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 
+0

Через любой стол заметного размера, используя процедурные функции, _far_ более неэффективно –

+0

Хорошая точка. Является ли построенный вызов Лен намного быстрее, чем функция, используемая функцией? –

+0

В большом масштабе записей, да. Хотя, конечно, вам придется протестировать большой набор записей с большими строками. Никогда не пишите ничего процедурного в SQL, если вы можете его избежать (т. Е. Петли) –

0

Darrel Lee Я думаю, что есть довольно хороший ответ. Заменить CHARINDEX() с PATINDEX(), и вы можете сделать некоторые слабые regex поиск по струне, тоже ...

Мол, что вы используете это для @pattern:

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

Почему вы, возможно, хотите сделать что-то сумасшедшее как это?

Допустим, вы загружаются с разделителями строк текста в промежуточную таблицу, где поле, содержащее данные, что-то вроде VARCHAR (8000) или NVARCHAR (макс) ...

Иногда бывает проще/быстрее сделать ELT (Extract-Load-Transform) с данными, а не ETL (Extract-Transform-Load), и один из способов сделать это - загрузить разделительные записи как есть в промежуточную таблицу, особенно если вам может понадобиться более простой способ видеть исключительные записи, а не рассматривать их как часть пакета SSIS ... но это священная война для другого потока.

4

У ответа @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, ',', '')) 

По какой-то причине, ЗАМЕНА избавляется от окончательной запятой, но и пространство перед ним (не знаю, почему). Это приводит к возвращаемому значению 5, когда вы ожидаете 4. Вот еще один способ сделать это, который будет работать даже в этом особом случае:

DECLARE @string VARCHAR(1000) 
SET @string = 'a,b,c,d ,' 
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string) 

Обратите внимание, что вам не нужно использовать звездочку. Любая двухсимвольная замена будет выполнена. Идея состоит в том, что вы удлиняете строку по одному символу для каждого экземпляра персонажа, который вы подсчитываете, а затем вычитаете длину оригинала. Это в основном противоположный метод первоначального ответа, который не приходит со странным побочным эффектом обрезки.

+5

«По какой-то причине REPLACE избавляется от последней запятой, но ТАКЖЕ пространство перед ним (не знаю, почему)». REPLACE не избавляется от последней запятой и пространства перед ним, это фактически функция LEN, которая игнорирует пустое пространство, возникающее в конце строки из-за этого пространства. –

2

Опираясь на @ решения Эндрю, вы получите гораздо более высокую производительность, используя непроцедурный табличное значение-функции и 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 
+0

Я использую эту функцию во многих моих устаревших базах данных, она очень помогает многим старым и ненастроенным базам данных. Экономит много времени и очень быстро работает даже на больших наборах данных. – Caimen

1

Принято ответ правильный, распространив его использовать 2 или более символов в подстроке:

Declare @string varchar(1000) 
Set @string = 'aa,bb,cc,dd' 
Set @substring = 'aa' 
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring) 
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 
     ); 
Смежные вопросы