获取两个特定字符之间的字符串

时间:2014-07-03 07:55:57

标签: sql-server sql-server-2008 sql-server-2008-r2

我有一个包含Filetext列的表文件列表,其中包含文本数据

create table fileslist  
(  
FileID int identity (1,1),  
Filetext nvarchar(max)  
) 

insert into fileslist select 'this file(''SQL_SCALAR1'') has been created to test your tricks of (''SQL_SCALAR2'')'   
insert into fileslist select 'this file(''SCALAR3'') created to test your tricks of (''SQL_SCALAR4'')'  
insert into fileslist select 'this file(''SQL_SCALAR5'') has been created to test your tricks of (''SQL_SCALAR6'')'   
insert into fileslist select 'this file(''SQL_7'') has been created'     
insert into fileslist select 'this file(''SQL_SCALAR8'') has been created to test your tricks of (''SQL_SCALAR9''), ohh i have more text than other (''SQL_SCALAR10'')files'    

我需要帮助才能逐个阅读所有记录,以便在两个特定字符之间提取字符串'('和')'和输出应

SQL_SCALAR1  
SQL_SCALAR2   
SCALAR3  
SQL_SCALAR4  
SQL_SCALAR5  
SQL_SCALAR6  
SQL_7  
SQL_SCALAR8  
SQL_SCALAR9  
SQL_SCALAR10  

4 个答案:

答案 0 :(得分:1)

我选择这种方法的原因是它可以处理每行搜索的动态数量的模式。

首先,我会使用Jeff Moden的字符串拆分器;

/*
 * Jeff Moden's famous string spliiter
 * http://www.sqlservercentral.com/articles/Tally+Table/72993/
 */

CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
        (@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
;

然后,我会写下面的内容;​​

SELECT  SUBSTRING   (
                        Item, 
                        2, 
                        CHARINDEX(')', Item) - 3
                    )
FROM FilesList
CROSS 
APPLY [dbo].[DelimitedSplit8K](FileText, '(')
WHERE Item LIKE '''SQL%'

根据评论的要求; (我对以这种方式屠杀杰夫的代码并不感到激动......)

CREATE FUNCTION [dbo].[DelimitedSplit8K_Butchered]
--===== Define I/O parameters
        (@pString VARCHAR(8000), @DelimiterA CHAR(1), @DelimiterB 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) = @DelimiterA
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@DelimiterA,@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.
OriginalOutput AS
(
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
)
    SELECT 
        Item    = SUBSTRING (
                                Item, 
                                1, 
                                CHARINDEX(@DelimiterB, Item) - 1
                            )
    FROM OriginalOutput 
    WHERE Item LIKE '%' + @DelimiterB + '%'
    --AND SUBSTRING(@pString, l.N1, l.L1) LIKE '%' + @DelimiterB + '%'
;
GO

使用如下;

SELECT  *
FROM FilesList
CROSS 
APPLY [dbo].[DelimitedSplit8K_Butchered](FileText, '(', ')')

答案 1 :(得分:0)

declare @ss varchar(100)= 'this file(''SQL_SCALAR1'') has been created to test your tricks of (''SQL_SCALAR2'')'   

不确定要显示的方式

SELECT  SUBSTRING ( (LEFT(@ss,CHARINDEX(')',@ss) - 1)),CHARINDEX('(',@ss)+1,LEN(LEFT(@ss,CHARINDEX(')',@ss) - 1))),
        SUBSTRING ((RIGHT(@ss,CHARINDEX('(',REVERSE(@ss)) - 1)),1,LEN(RIGHT(@ss,CHARINDEX('(',REVERSE(@ss)) - 1))-1)

OR

SELECT  SUBSTRING ( (LEFT(@ss,CHARINDEX(')',@ss) - 1)),CHARINDEX('(',@ss)+1,LEN(LEFT(@ss,CHARINDEX(')',@ss) - 1)))
Union
SELECT  SUBSTRING ((RIGHT(@ss,CHARINDEX('(',REVERSE(@ss)) - 1)),1,LEN(RIGHT(@ss,CHARINDEX('(',REVERSE(@ss)) - 1))-1)

答案 2 :(得分:0)

这是你正在寻找的解决方案,欢呼....

;WITH CTE AS
(
SELECT SUBSTRING(FILETEXT,CHARINDEX('(',FileText,1)+2,11) 'Result'  
FROM FILESLIST
UNION ALL
SELECT * FROM
(
    SELECT 
    SUBSTRING(
    (SUBSTRING(FILETEXT,CHARINDEX(')',FILETEXT,1)+1,LEN(FILETEXT)-               
    CHARINDEX(')',FILETEXT,1))  ) 
    ,CHARINDEX('(',(SUBSTRING(FILETEXT,CHARINDEX(')',FILETEXT,1)+1,LEN(FILETEXT)-
    CHARINDEX(')',FILETEXT,1))  ) ,1)+2,11
) 'Result'
FROM FILESLIST
) T1
WHERE Result like '%SQL%'
)
SELECT * FROM CTE
ORDER BY RESULT ASC

答案 3 :(得分:0)

递归CTE的解决方案是

WITH REP AS (
  SELECT FileID
       , Filetext = REPLACE(Filetext, ')', '(')
  FROM   fileslist
), Splitter AS (
  SELECT FileID
       , FileText
       , NextStart = CHARINDEX('(', FileText)
       , Pos = 0
       , Token = CAST(SubString(FileText, 0, CHARINDEX('(', FileText)) 
                      AS NVarchar(50))
  FROM   REP
  UNION ALL
  SELECT FileId
       , FileText
       , NextStart = CHARINDEX('(', FileText, NextStart + 1)
       , Pos = Pos + 1
       , Token = CAST(SubString(FileText, NextStart + 1
                              , CHARINDEX('(', FileText, NextStart + 1) 
                              - NextStart - 1) AS NVarchar(50))
  FROM   Splitter
  WHERE  CHARINDEX('(', FileText, NextStart + 1) - NextStart - 1 > 0
)
SELECT FileID, Token
FROM   REC
WHERE  Pos % 2 = 1
order by FileID, Pos

Splitter CTE使用单个分隔符拆分行,因此在使用之前,我们需要使两个分隔符相同。主查询只返回奇数标记,因为偶数标记是字符串的其他部分,例如字符'this file(''SQL_7'') has been created'将由Splitter返回(仅包含有趣的列):< / p>

Pos | Token
----+------------------
  0 | this file
  1 | SQL_7
  2 | has been created