Search code examples
sqlt-sqlfor-loopsubstringpatindex

SQL loop to find all occurrences of a string expression in a DB field


I have a varchar(max) field in an SQL Server DB table and I would like to find all occurrences of a string within this field and 50 characters either side to give it context when reading.

I have done this using the code below, but now i would like to adapt it to do it for all occurrences of a string, and for it to appear in one field.

declare @extract varchar(max)
set @extract = 
'As Harry squelched along the deserted corridor he came across somebody who looked just as preoccupied as he was. Nearly Headless Nick, the ghost of Gryffindor Tower, was staring morosely out of a window, muttering under his breath, ". . . don''t fulfill their requirements . . . half an inch, if that . . ." 
"Hello, Nick," said Harry. 

"Hello, hello," said Nearly Headless Nick, starting and looking round. He wore a dashing, plumed hat on his long curly hair, and a tunic with a ruff, which concealed the fact that his neck was almost completely severed. He was pale as smoke, and Harry could see right through him to the dark sky and torrential rain outside. 

"You look troubled, young Potter," said Nick, folding a transparent letter as he spoke and tucking it inside his doublet. 

"So do you," said Harry.'

declare @searchterm varchar(max)
set @searchterm = '%Harry%'

declare @searchtermlength int = len(@searchterm)
declare @stringFrom int = (select PATINDEX(@searchterm,@extract) - 50 )
declare @stringto int = (select PATINDEX(@searchterm,@extract) + @searchtermlength + 50 )
declare @noChars int = @stringto - @stringfrom

select SUBSTRING(@extract,@stringFrom,@noChars)

This returns 'As Harry squelched along the deserted corridor he came acros' but I would like it to return:

As Harry squelched along the deserted corridor he came acros...
...f an inch, if that . . ." "Hello, Nick," said Harry. "Hello, hello," said Nearly Headless Nick, sta
...ost completely severed. He was pale as smoke, and Harry could see right through him to the dark sky and tor...
...king it inside his doublet. "So do you," said Harry.

I'm guessing that this can be done using a loop but I've never really used one before so any help would be greatly appreciated.

Thanks in advance


Solution

  • Hope this is what you are looking at

    DECLARE @extract NVARCHAR(MAX)
    DECLARE @out_table TABLE (string NVARCHAR(MAX))
    DECLARE @search_string NVARCHAR(20)
    DECLARE @pos INT
    DECLARE @start_pos INT
    DECLARE @trim_len INT
    DECLARE @string_len INT
    DECLARE @search_string_len INT
    DECLARE @old_pos INT
    DECLARE @start_dots NVARCHAR(3)
    DECLARE @end_dots NVARCHAR(3)
    
    SET @extract ='As Harry squelched along the deserted corridor he came across somebody who looked just as preoccupied as he was. Nearly Headless Nick, the ghost of Gryffindor Tower, was staring morosely out of a window, muttering under his breath, ". . . don''t fulfill their requirements . . . half an inch, if that . . ." 
    "Hello, Nick," said Harry. 
    "Hello, hello," said Nearly Headless Nick, starting and looking round. He wore a dashing, plumed hat on his long curly hair, and a tunic with a ruff, which concealed the fact that his neck was almost completely severed. He was pale as smoke, and Harry could see right through him to the dark sky and torrential rain outside. 
    "You look troubled, young Potter," said Nick, folding a transparent letter as he spoke and tucking it inside his doublet. 
    "So do you," said Harry.'
    
    SET @search_string = 'Harry'
    SET @string_len = LEN(@extract)
    SET @search_string_len = LEN(@search_string)
    SET @old_pos = 0
    SET @pos = PATINDEX('%' + @search_string + '%',@extract) 
    
    WHILE @pos > 0 AND @old_pos <> @pos
    BEGIN
    
        SET @start_dots = '...'
        SET @end_dots = '...'
    
        IF (@pos - 50) < 1
        BEGIN
            SET @start_pos = 1
            SET @start_dots = ''
        END
        ELSE
        BEGIN
            SET @start_pos = @pos - 47  -- 3 chars for the dots
        END
    
        IF (@pos + @search_string_len + 50) > @string_len
        BEGIN
            SET @trim_len = (@string_len - @start_pos) + 1
            SET @end_dots = ''
        END
        ELSE
        BEGIN
            SET @trim_len = (@pos - @start_pos) + @search_string_len + 47  -- 3 chars for the dots
        END
    
        INSERT INTO @out_table
        SELECT @start_dots + SUBSTRING(@extract, @start_pos, @trim_len) + @end_dots
    
        SET @old_pos = @pos
        SET @pos = PATINDEX('%' + @search_string + '%',SUBSTRING(@extract, @pos + 1,LEN(@extract))) + @pos
    END
    
    SELECT * FROM @out_table
    

    Output

    As Harry squelched along the deserted corridor he came ...
    ... an inch, if that . . ."   "Hello, Nick," said Harry.   "Hello, hello," said Nearly Headless Nick, ...
    ... completely severed. He was pale as smoke, and Harry could see right through him to the dark sky an...
    ...ing it inside his doublet.   "So do you," said Harry.