SQL Server - найти n-е вхождение в строке - PullRequest
28 голосов
/ 04 января 2012

У меня есть столбец таблицы, который содержит такие значения, как abc_1_2_3_4.gif или zzz_12_3_3_45.gif и т. Д.

Я хочу найти индекс каждого подчеркивания _ в приведенных выше значениях.Будет только четыре подчеркивания, но, учитывая, что они могут находиться в любом месте строки, как я могу этого добиться?

Я пробовал подстроку и charindex , но я могу надежно получить только первую.Есть идеи?

Ответы [ 17 ]

1 голос
/ 07 сентября 2015

Я сделал это, создав несколько отдельных пользовательских функций, по одной для каждой позиции искомого символа, т.е. 2-го, 3-го:

CREATE FUNCTION [dbo]. [FnCHARPOS2] (@SEARCHCHAR VARCHAR (255), @SEARCHSTRINGVARCHAR (255)) ВОЗВРАЩАЕТСЯ INT, КАК НАЧИНАЕТ ВОЗВРАТ CHARINDEX (@ SEARCHCHAR, @ SEARCHSTRING (CHARINDEX (@ SEARCHCHAR, @ SEARCHSTRING, 0) +1));

CREATE FUNCTION [dbo].[fnCHARPOS3]
(@SEARCHCHAR VARCHAR(255),
@SEARCHSTRING VARCHAR(255))
RETURNS INT
AS
BEGIN
 RETURN CHARINDEX(@SEARCHCHAR,@SEARCHSTRING,    (CHARINDEX(@SEARCHCHAR,@SEARCHSTRING,    (CHARINDEX(@SEARCHCHAR,@SEARCHSTRING,0)+1)))+1);

Затем можно передать как параметрсимвол, который вы ищете, и строка, в которой вы ищете:

Так что, если вы искали 'f' и хотели узнать позицию первых 3 вхождений:

select 
database.dbo.fnCHARPOS2('f',tablename.columnname),
database.dbo.fnCHARPOS3('f',tablename.columnname)
from tablename

Это сработалодля меня!

0 голосов
/ 14 сентября 2018

Вдохновленный ответом Алекса К. Однажды (2k8) я создал скрипт для функции токена для SQL Server для возврата определенного токена из строки. Мне это понадобилось для реорганизации пакета SSIS в T-SQL без необходимости вручную выполнять решение Алекса несколько раз. У моей функции есть один недостаток: она возвращает значение токена в виде таблицы (один столбец, одна строка), а не в качестве значения varchar. Если у кого-то есть решение для этого, пожалуйста, дайте мне знать.

DROP FUNCTION [RDW].[token]
GO

create function [RDW].[token] (@string varchar(8000), @split varchar(50), @returnIndex int) 
returns table  
as 
    return with T(img, starts, pos, [index]) as ( 
        select @string, 1, charindex(@split, @string), 0 
        union all 
        select @string, pos + 1, charindex(@split, @string, pos + 1), [index]+1 
        from t 
        where pos > 0
    )
    select substring(img, starts, case when pos > 0 then pos - starts else len(img) end) token
    from T
    where [index] = @returnIndex 
GO
0 голосов
/ 07 февраля 2018
DECLARE @x VARCHAR(32) = 'MS-SQL-Server';

SELECT 
SUBSTRING(@x,0,CHARINDEX('-',LTRIM(RTRIM(@x)))) A,
SUBSTRING(@x,CHARINDEX('-',LTRIM(RTRIM(@x)))+1,CHARINDEX('-' 
,LTRIM(RTRIM(@x)))) B,
SUBSTRING(@x,CHARINDEX('-',REVERSE(LTRIM(RTRIM(@x))))+1,LEN(@x)-1) C


A   B   C
MS  SQL Server
0 голосов
/ 20 июля 2017
declare @a nvarchar(50)='Enter Your string '
declare @character char='e'
declare @nthoccurence int = 2
declare @i int = 1
declare @j int =0
declare @count int = len(@a)-len(replace(@a,@character,''))

if(@count >= @nthoccurence)
begin
        while (@I <= @nthoccurence)
        begin
            set @j= CHARINDEX(@character,@a,@j+1)
            set @i= @i+1
        end
        print @j
end
else
    Print 'you have only '+convert(nvarchar ,@count)+' occurrences of '+@character
end
0 голосов
/ 23 января 2014

Я использовал функцию, чтобы с большим успехом получить «n-й» элемент из поля строки с разделителями. Как упомянуто выше, это не «быстрый» способ иметь дело с вещами, но он, конечно, очень удобен.

create function GetArrayIndex(@delimited nvarchar(max), @index int,  @delimiter nvarchar(100) = ',')  returns nvarchar(max)  
as    
begin     
 declare @xml xml, @result nvarchar(max)  
 set @xml = N'<root><r>' + replace(@delimited, @delimiter,'</r><r>') + '</r></root>'  
 select @result = r.value('.','varchar(max)')   
 from @xml.nodes('//root/r[sql:variable("@index")]') as records(r)  

 return @result   
end    
0 голосов
/ 22 июля 2015

Простой пример, чтобы сделать это с преобразованием XML:

SELECT 'A|B|C'
     , concat('<x>', REPLACE('A|B|C', '|', '</x><x>'), '</x>')
     , cast(concat('<x>', REPLACE('A|B|C', '|', '</x><x>'), '</x>') as xml).query('/x[2]')
     , cast(concat('<x>', REPLACE('A|B|C', '|', '</x><x>'), '</x>') as xml).value('/x[2]',     
       'varchar');

А вот перевод для вашего образца:

SELECT gifname
      ,cast(concat('<x>', REPLACE(gifname, '_', '</x><x>'), '</x>') as xml).query('/x[2]') as xmlelement
     , cast(concat('<x>', REPLACE(gifname, '_', '</x><x>'), '</x>') as xml).value('/x[2]', 'varchar(10)') as result
    FROM (
      SELECT 'abc_1_2_3_4.gif' as gifname
      UNION ALL
      SELECT 'zzz_12_3_3_45.gif'
    ) tmp
0 голосов
/ 11 октября 2016

Я играл быстрее, чем просто перебирал строку.

CREATE FUNCTION [ssf_GetNthSeparatorPosition] ( @TargetString VARCHAR(MAX)
                                              , @Sep VARCHAR(25)
                                              , @n INTEGER )
RETURNS INTEGER
/****************************************************************************************
--#############################################################################
-- Returns the position of the Nth Charactor sequence
--                                     1234567890123456789
-- Declare @thatString varchar(max) = 'hi,there,jay,yo'
  Select dbo.ssf_GetNthSeparatorPosition(@thatString, ',', 3) --would return 13
--############################################################################ 


****************************************************************************************/
AS
    BEGIN
        DECLARE @Retval INTEGER = 0
        DECLARE @CurPos INTEGER = 0
        DECLARE @LenSep INTEGER = LEN(@Sep)

        SELECT @CurPos = CHARINDEX(@Sep, @TargetString)

        IF ISNULL(@LenSep, 0) > 0
            AND @CurPos > 0
            BEGIN

               SELECT @CurPos = 0
              ;with lv0 AS (SELECT 0 g UNION ALL SELECT 0)
                            ,lv1 AS (SELECT 0 g FROM lv0 a CROSS JOIN lv0 b) -- 4
                            ,lv2 AS (SELECT 0 g FROM lv1 a CROSS JOIN lv1 b) -- 16
                            ,lv3 AS (SELECT 0 g FROM lv2 a CROSS JOIN lv2 b) -- 256
                            ,lv4 AS (SELECT 0 g FROM lv3 a CROSS JOIN lv3 b) -- 65,536
                            ,lv5 AS (SELECT 0 g FROM lv4 a CROSS JOIN lv4 b) -- 4,294,967,296
                            ,Tally (n) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM lv5),
                        results
                          AS ( SELECT n - LEN(@Sep) AS Nth
                                ,   row_number() OVER ( ORDER BY n ) - 1 AS Position
                                FROM Tally t
                                WHERE n BETWEEN 1
                                        AND     DATALENGTH(@TargetString) + DATALENGTH(@Sep)
                                    AND SUBSTRING(@Sep + @TargetString, n, LEN(@Sep)) = @Sep)
                    SELECT @CurPos = Nth
                        FROM results
                        WHERE results.Position = @n


            END
        RETURN @CurPos

    END

GO
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...