문제

마크다운으로 작성된 테이블 값이 있어서 웹 브라우저에 렌더링된 HTML로 표시되므로 SQL에서 임시 프로시저를 생성하는 중입니다. (마크다운을 HTML로 변환).

열의 문자열은 현재 다음과 같습니다.

Questions about **general computing hardware and software** are off-topic for Stack Overflow unless they directly involve tools used primarily for programming. You may be able to get help on [Super User](http://superuser.com/about)

저는 현재 볼드체와 이탤릭체 텍스트로 작업하고 있습니다.이거는 (굵은 글씨의 경우) 홀수 N번 패턴을 교체해야 합니다.**~와 함께<b>그리고 심지어는</b>.
나는 보았다 바꾸다() 그러나 문자열의 모든 패턴에 대해 교체를 수행합니다.

그렇다면 홀수 또는 짝수인 경우에만 하위 문자열을 어떻게 바꿀 수 있습니까?

업데이트: 어떤 사람들은 내가 어떤 스키마를 사용하고 있는지 궁금해하므로 한번 살펴보세요. 여기.

원하는 경우 하나 더 추가: html 하이퍼링크에 대한 마크다운 스타일 하이퍼링크는 그렇게 단순해 보이지 않습니다.

도움이 되었습니까?

해결책

사용하여STUFF기능과 간단한WHILE고리:

CREATE FUNCTION dbo.fn_OddEvenReplace(@text nvarchar(500), 
                                      @textToReplace nvarchar(10), 
                                      @oddText nvarchar(10), 
                                      @evenText nvarchar(500))
RETURNS varchar(max)
AS
BEGIN
    DECLARE @counter tinyint
    SET @counter = 1

    DECLARE @switchText nvarchar(10)
    WHILE CHARINDEX(@textToReplace, @text, 1) > 0
    BEGIN
        SELECT @text = STUFF(@text, 
                    CHARINDEX(@textToReplace, @text, 1), 
                    LEN(@textToReplace), 
                    IIF(@counter%2=0,@evenText,@oddText)),
                @counter = @counter + 1
    END
    RETURN @text
END

그리고 다음과 같이 사용할 수 있습니다.

SELECT dbo.fn_OddEvenReplace(column, '**', '<b>', '</b>')
FROM table

업데이트:

이는 SP로 다시 작성됩니다.

CREATE PROC dbo.##sp_OddEvenReplace @text nvarchar(500), 
                                  @textToReplace nvarchar(10), 
                                  @oddText nvarchar(10), 
                                  @evenText nvarchar(10),
                                  @returnText nvarchar(500) output
AS
BEGIN
    DECLARE @counter tinyint
    SET @counter = 1

    DECLARE @switchText nvarchar(10)
    WHILE CHARINDEX(@textToReplace, @text, 1) > 0
    BEGIN
        SELECT @text = STUFF(@text, 
                    CHARINDEX(@textToReplace, @text, 1), 
                    LEN(@textToReplace), 
                    IIF(@counter%2=0,@evenText,@oddText)),
                @counter = @counter + 1
    END
    SET @returnText = @text
END
GO

그리고 실행하려면:

DECLARE @returnText nvarchar(500)
EXEC dbo.##sp_OddEvenReplace '**a** **b** **c**', '**', '<b>', '</b>', @returnText output

SELECT @returnText

다른 팁

OP의 요청에 따라 임시 저장 프로시저로 수행되도록 이전 답변을 수정했습니다.문자열 테이블에 대한 사용법도 유용하다고 생각하여 이전 답변을 남겼습니다.

8000개 이상의 값을 가진 Tally(또는 숫자) 테이블이 이미 존재하는 것으로 알려진 경우 CTE의 표시된 섹션을 생략하고 CTE 참조를 생략할 수 있습니다. 계인 기존 Tally 테이블의 이름으로 대체됩니다.

create procedure #HtmlTagExpander(
     @InString   varchar(8000) 
    ,@OutString  varchar(8000)  output
)as 
begin
    declare @Delimiter  char(2) = '**';

    create table #t( 
         StartLocation  int             not null
        ,EndLocation    int             not null

        ,constraint PK unique clustered (StartLocation desc)
    );

    with 
          -- vvv Only needed in absence of Tally table vvv
    E1(N) as ( 
        select 1 from (values
            (1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1)
        ) E1(N)
    ),                                              --10E+1 or 10 rows
    E2(N) as (select 1 from E1 a cross join E1 b),  --10E+2 or 100 rows
    E4(N) As (select 1 from E2 a cross join E2 b),  --10E+4 or 10,000 rows max
    tally(N) as (select row_number() over (order by (select null)) from E4),
          -- ^^^ Only needed in absence of Tally table ^^^

    Delimiter as (
        select len(@Delimiter)     as Length,
               len(@Delimiter)-1   as Offset
    ),
    cteTally(N) AS (
        select top (isnull(datalength(@InString),0)) 
            row_number() over (order by (select null)) 
        from tally
    ),
    cteStart(N1) AS 
        select 
            t.N 
        from cteTally t cross join Delimiter 
        where substring(@InString, t.N, Delimiter.Length) = @Delimiter
    ),
    cteValues as (
        select
             TagNumber = row_number() over(order by N1)
            ,Location   = N1
        from cteStart
    ),
    HtmlTagSpotter as (
        select
             TagNumber
            ,Location
        from cteValues
    ),
    tags as (
        select 
             Location       = f.Location
            ,IsOpen         = cast((TagNumber % 2) as bit)
            ,Occurrence     = TagNumber
        from HtmlTagSpotter f
    )
    insert #t(StartLocation,EndLocation)
    select 
         prev.Location
        ,data.Location
    from tags data
    join tags prev
       on prev.Occurrence = data.Occurrence - 1
      and prev.IsOpen     = 1;

    set @outString = @Instring;

    update this
    set @outString = stuff(stuff(@outString,this.EndLocation,  2,'</b>')
                                           ,this.StartLocation,2,'<b>')
    from #t this with (tablockx)
    option (maxdop 1);
end
go

다음과 같이 호출됩니다.

declare @InString   varchar(8000) 
       ,@OutString  varchar(8000);

set @inString = 'Questions about **general computing hardware and software** are off-topic **for Stack Overflow.';
exec #HtmlTagExpander @InString,@OutString out; select @OutString;

set @inString = 'Questions **about** general computing hardware and software **are off-topic** for Stack Overflow.';
exec #HtmlTagExpander @InString,@OutString out; select @OutString;
go

drop procedure #HtmlTagExpander;
go

출력은 다음과 같습니다.

Questions about <b>general computing hardware and software</b> are off-topic **for Stack Overflow.

Questions <b>about</b> general computing hardware and software <b>are off-topic</b> for Stack Overflow.

한 가지 옵션은 정규식을 사용하는 것입니다. 정규식을 사용하면 이러한 패턴을 매우 간단하게 바꿀 수 있습니다.RegEx 함수는 SQL Server에 내장되어 있지 않으므로 직접 컴파일하거나 기존 라이브러리에서 SQL CLR을 사용해야 합니다.

이 예에서는 SQL#(SQL샤프) 내가 만든 라이브러리이지만 RegEx 기능은 무료 버전에서 사용할 수 있습니다.

SELECT SQL#.RegEx_Replace
(
   N'Questions about **general computing hardware and software** are off-topic\
for Stack Overflow unless **they** directly involve tools used primarily for\
**programming. You may be able to get help on [Super User]\
(https://superuser.com/about)', -- @ExpressionToValidate
   N'\*\*([^\*]*)\*\*', -- @RegularExpression
   N'<b>$1</b>', -- @Replacement
   -1, -- @Count (-1 = all)
   1, - @StartAt
   'IgnoreCase' -- @RegEx options
);

위의 패턴 \*\*([^\*]*)\*\* 이중 별표로 둘러싸인 항목을 찾으면 됩니다.이 경우 홀수/짝수에 대해 걱정할 필요가 없습니다.이는 또한 잘못된 형태의 결과를 얻지 못할 것임을 의미합니다. <b>-어떤 이유로든 추가 항목이 있는 경우에만 태그 ** 문자열에서.원래 문자열에 두 가지 추가 테스트 사례를 추가했습니다.완전한 세트 ** 단어 주위에 they 그리고 비교할 수 없는 세트 ** 그 단어 바로 앞에 programming.출력은 다음과 같습니다

Questions about <b>general computing hardware and software</b> are off-topicfor Stack Overflow unless <b>they</b> directly involve tools used primarily for **programming. You may be able to get help on [Super User](https://superuser.com/about)

이는 다음과 같이 렌더링됩니다.

에 관한 질문 일반 컴퓨팅 하드웨어 및 소프트웨어 스택 오버플로에 대한 주제와 관련이 없습니다. 그들 **프로그래밍에 주로 사용되는 도구와 직접적으로 관련됩니다.에 대한 도움을 받을 수도 있습니다. 슈퍼유저

이 솔루션은 Jeff Moden이 설명한 기술을 사용합니다. SQL의 Running Sum 문제에 대한 이 기사.이 솔루션은 시간이 오래 걸리지만 기발한 업데이트 SQL Server에서는 클러스터형 인덱스를 통해 커서 기반 솔루션보다 대규모 데이터 세트에 대해 훨씬 더 효율적이라는 약속을 갖고 있습니다.

업데이트 - 문자열 테이블을 작동하도록 아래 수정됨

다음과 같이 생성된 집계 테이블이 있다고 가정합니다(최소 8000개 행 포함).

create table dbo.tally (
     N int not null
    ,unique clustered (N desc)
);
go

with 
E1(N) as ( 
    select 1 from (values
        (1),(1),(1),(1),(1),
        (1),(1),(1),(1),(1)
    ) E1(N)
),                                              --10E+1 or 10 rows
E2(N) as (select 1 from E1 a cross join E1 b),  --10E+2 or 100 rows
E4(N) As (select 1 from E2 a cross join E2 b)   --10E+4 or 10,000 rows max
insert dbo.tally(N)
select row_number() over (order by (select null)) from E4;
go

그리고 HtmlTagSpotter 함수는 다음과 같이 정의됩니다.

create function dbo.HtmlTagSPotter(
     @pString       varchar(8000)
    ,@pDelimiter    char(2))
returns table with schemabinding as
return
   WITH 
        Delimiter as (
        select len(@pDelimiter)     as Length,
               len(@pDelimiter)-1   as Offset
    ),
    cteTally(N) AS (
        select top (isnull(datalength(@pstring),0)) 
            row_number() over (order by (select null)) 
        from dbo.tally
    ),
    cteStart(N1) AS (--==== Returns starting position of each "delimiter" )
        select 
            t.N 
        from cteTally t cross join Delimiter 
        where substring(@pString, t.N, Delimiter.Length) = @pDelimiter
    ),
    cteValues as (
        select
             ItemNumber = row_number() over(order by N1)
            ,Location   = N1
        from cteStart
    )
    select
         ItemNumber
        ,Location
    from cteValues
go

그런 다음 다음 SQL을 실행하면 필요한 대체가 수행됩니다.끝에 있는 내부 조인은 뒤에 오는 "홀수" 태그가 변환되는 것을 방지합니다.

create table #t( 
     ItemNo         int             not null
    ,Item           varchar(8000)       null
    ,StartLocation  int             not null
    ,EndLocation    int             not null

    ,constraint PK unique clustered (ItemNo,StartLocation desc)
);

with data(i,s) as ( select i,s from (values
        (1,'Questions about **general computing hardware and software** are off-topic **for Stack Overflow.')
       ,(2,'Questions **about **general computing hardware and software** are off-topic **for Stack Overflow.')
          --....,....1....,....2....,....3....,....4....,....5....,....6....,....7....,....8....,....9....,....0
    )data(i,s)
),
tags as (
    select 
         ItemNo         = data.i
        ,Item           = data.s
        ,Location       = f.Location
        ,IsOpen         = cast((TagNumber % 2) as bit)
        ,Occurrence     = TagNumber
    from data
    cross apply dbo.HtmlTagSPotter(data.s,'**') f
)
insert #t(ItemNo,Item,StartLocation,EndLocation)
select 
     data.ItemNo
    ,data.Item
    ,prev.Location
    ,data.Location
from tags data
join tags prev
   on prev.ItemNo       = data.ItemNo
  and prev.Occurrence = data.Occurrence - 1
  and prev.IsOpen     = 1

union all

select 
    i,s,8001,8002
from data
;

declare @ItemNo     int
       ,@ThisStting varchar(8000);

declare @s varchar(8000);
update this
    set @s = this.Item = case when this.StartLocation > 8000
                              then this.Item
                              else stuff(stuff(@s,this.EndLocation,  2,'</b>')
                                                 ,this.StartLocation,2,'<b>')
                         end
from #t this with (tablockx)
option (maxdop 1);

select
    Item
from (
    select 
         Item
        ,ROW_NUMBER() over (partition by ItemNo order by StartLocation) as rn
    from #t
) t
where rn = 1
go

굽힐 수 있는:

Item
------------------------------------------------------------------------------------------------------------
Questions about <b>general computing hardware and software</b> are off-topic **for Stack Overflow.
Questions <b>about </b>general computing hardware and software<b> are off-topic </b>for Stack Overflow.
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top