代码之家  ›  专栏  ›  技术社区  ›  Jake

将字符串的所有实例提取到连接的结果中

  •  2
  • Jake  · 技术社区  · 7 年前

    使用sql server 2014,我希望在一个字段中搜索并返回找到的字符串的所有实例,外加以下单词。例如,列中的文本可以是:

    “exec sproc1,然后在此处执行更多文本,然后可能执行sproc2 exec storedproc3和exec sproc1”

    我想优雅地返回“sproc1,sproc2,storedpoc3,sproc1”,因为每个词都是exec或execute后面的单词(用空格分隔)。如您在示例中所见,前导字可能会有所不同,存储过程名称的长度也可能会有所不同。我已经能够返回exec/execute的第一个用法;我的问题是有时有多个用法(见下文)。

    REPLACE(REPLACE(CASE
            WHEN [sJSTP].[subsystem]='TSQL' AND CHARINDEX('EXECUTE',[sJSTP].[command],1)>0
                THEN SUBSTRING([sJSTP].[command],CHARINDEX('EXECUTE',[sJSTP].[command],1)+8,
                    IIF(
                    CHARINDEX(' ',[sJSTP].[command],CHARINDEX('EXECUTE',[sJSTP].[command],1)+8)>0,
                    CHARINDEX(' ',[sJSTP].[command],CHARINDEX('EXECUTE',[sJSTP].[command],1)+8)-CHARINDEX('EXECUTE',[sJSTP].[command],1)-8,
                    LEN([sJSTP].[command])))
            WHEN [sJSTP].[subsystem]='TSQL' AND CHARINDEX('EXEC',[sJSTP].[command],1)>0 AND CHARINDEX('DCEXEC',[sJSTP].[command],1)<=0
                THEN SUBSTRING([sJSTP].[command],CHARINDEX('EXEC',[sJSTP].[command],1)+5,
                    IIF(
                    CHARINDEX(' ',[sJSTP].[command],CHARINDEX('EXEC',[sJSTP].[command],1)+5)>0,
                    CHARINDEX(' ',[sJSTP].[command],CHARINDEX('EXEC',[sJSTP].[command],1)+5)-CHARINDEX('EXEC',[sJSTP].[command],1)-5,
                    LEN([sJSTP].[command])))
        END,'[',''),']','') AS sprocname
    

    它的最终用途是解析msdb..sysjobsteps表中的作业命令,以查看正在使用的存储过程。

    编辑: 添加样本数据

    样本1: 执行季度第1期,“BW” 执行季度运行'qr' 执行官季刊第2期,“VAS” 执行季度第1期“wr” 执行季度第3期,“RW” “ASF”季度执行官 执行季报

    样本2: 声明@rundate datetime,@rptqtr datetime,@qtr int

    设置@rundate=getdate() 设置@rptqtr='06/30/2016'

    设置@qtr=(选择datediff(季度,@rptqtr,@rundate))

    执行季度摘录@qtr

    样本3: 执行每日同步进程 执行日常流程

    2 回复  |  直到 7 年前
        1
  •  1
  •   John Cappelletti    7 年前

    只是另一个内联选项,不限于8K

    例子

    Declare @YourTable table (ID int,SomeCol varchar(max))
    Insert into @YourTable values
     (1,'exec quarterly_run 1, ''BW'' exec quarterly_run_2 1, ''QR '' exec quarterly_run 2, ''VAS'' exec quarterly_run 1, ''WR'' exec quarterly_run 3, ''RW'' exec quarterly_run_2 1, ''ASF'' exec quarterly_run_3 1, ''ALL''')
    ,(2,'declare @rundate datetime, @rptqtr datetime, @qtr int
    
    set @rundate = getdate() set @rptqtr = ''06/30/2016''
    
    set @qtr = (select datediff(quarter,@rptqtr,@rundate))
    
    exec quarterly_extract @qtr
    ')
    ,(3,'exec Daily_Sync_Process exec Daily_Process')
    
    ;with cte as (
    Select A.ID
          ,C.*
     From  @YourTable A
     Cross Apply (values (replace(replace(SomeCol,char(13),' '),char(10),' '))) B(CleanString)
     Cross Apply (
                    Select RetSeq,RetVal = case when Lag(RetVal,1) over (Order by RetSeq) in ('Exec','Execute') then RetVal else null end
                    From (
                            Select RetSeq = Row_Number() over (Order By (Select null))
                                  ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                            From  (Select x = Cast('<x>' + replace((Select replace(CleanString,' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
                            Cross Apply x.nodes('x') AS B(i)
                         ) C1
                 ) C
    )
    Select A.ID
          ,NewString = Stuff((Select ', ' +RetVal From cte Where ID=A.ID Order By RetSeq For XML Path ('')),1,2,'') 
     From cte A
     Group By A.ID
    

    退换商品

    ID  NewString
    1   quarterly_run, quarterly_run_2, quarterly_run, quarterly_run, quarterly_run, quarterly_run_2, quarterly_run_3
    2   quarterly_extract
    3   Daily_Sync_Process, Daily_Process
    
        2
  •  1
  •   S3S    7 年前

    所以如果你想马上得到 exec 然后我在空间上分裂,然后使用自连接。下面是使用以下函数的代码,它是 Jeff Moden's splitter.

    with cte as(
    select
        job_id
        ,step_name
        ,step_id
        ,s.ItemNumber
        ,s.Item
    from msdb..sysjobsteps
    --split on the space
    cross apply dbo.DelimitedSplit8K(command,' ') s)
    
    select 
        c.job_id
        ,c.step_id
        ,c.step_name
        ,c.Item
        ,c2.Item
    from cte c
    --self join to get exec myproc in the same row
    full join
        cte c2 on 
        c2.ItemNumber = c.ItemNumber + 1 
        and c.job_id = c2.job_id
        and c.step_id = c2.step_id
    --we only care where the base table has exec or execute (not executed, etc)
    where c.Item = 'exec' or c.Item = 'execute'
    order by 
        c.job_id, c.step_id, c.ItemNumber
    

    当命令 exec someproc 有两个空间。你可以用一个 replace() 但您必须多次嵌套此替换以尽可能多地占用空间。你可以在 command 拆分器函数中的列

    --here we replace two spaces with 1 for the entire command
    cross apply dbo.DelimitedSplit8K(replace(command,'  ',' '),' ') s)
    

    拆分器函数

    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    
    CREATE FUNCTION [dbo].[DelimitedSplit8K] (@pString VARCHAR(8000), @pDelimiter CHAR(1))
    --WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!
    
    RETURNS TABLE WITH SCHEMABINDING AS
    RETURN
    
    /* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000... enough to cover VARCHAR(8000)*/
    
      WITH E1(N) AS (
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                    ),                          --10E+1 or 10 rows
           E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
           E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
     cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                         -- for both a performance gain and prevention of accidental "overruns"
                     SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                    ),
    cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                     SELECT 1 UNION ALL
                     SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                    ),
    cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                     SELECT s.N1,
                            ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                       FROM cteStart s
                    )
    --===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
     SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
            Item       = SUBSTRING(@pString, l.N1, l.L1)
       FROM cteLen l
    
    
    GO