在SQL中提取双引号之间的字符串

时间:2018-07-26 13:17:18

标签: sql sql-server tsql

我有一个字符串,我需要返回双引号之间的字符

  

'(((“名称​​1”和“名称2”)或“名称3”)

我需要回来

Name 1
Name 2
Name 3

我已经使用下面的函数来分割字符串,但是我得到了((和Or与AND等..这是我不想要的,但不幸的是,我无法确定所有其他可能的字符包含在内,因此删除或替换它们实际上是不可行的。

ALTER FUNCTION [dbo].[fn_SplitString] 
( 
   @string NVARCHAR(MAX), 
   @delimiter CHAR(1) 
) 
RETURNS @output TABLE(ID int, splitdata NVARCHAR(MAX) 
) 
BEGIN 
   DECLARE @start INT, @end INT, @Count INT
   set @Count = 1
   SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
   WHILE @start < LEN(@string) + 1 BEGIN 
      IF @end = 0  
         SET @end = LEN(@string) + 1

       INSERT INTO @output (ID, splitdata)  
       VALUES(@Count, SUBSTRING(@string, @start, @end - @start)) 
       SET @start = @end + 1 
       SET @end = CHARINDEX(@delimiter, @string, @start)
       Set @Count = @Count+1
   END 
   RETURN 
END

我知道这段代码将返回2个分隔符之间的字符串

substring( LEFT(@String, charindex(']', @String)-1), CHARINDEX('[', @String) + len('['), LEN(@String)) 

有什么方法可以合并2并返回所需的输出?

谢谢

4 个答案:

答案 0 :(得分:5)

这是一种递归方法

DECLARE @s VARCHAR(100)='(("Name 1" and "Name 2") or "Name 3")';

WITH recCTE AS
(
    SELECT 1 AS Position
          ,SUBSTRING(@s,1,1) AS CharAtPos
          ,CASE WHEN SUBSTRING(@s,1,1)='"' THEN 0 ELSE -1 END AS QuoteGroup
          ,CASE WHEN SUBSTRING(@s,1,1)='"' THEN 1 ELSE 0 END AS QuoteIsOpen
    UNION ALL
    SELECT r.Position+1
          ,SUBSTRING(@s,r.Position+1,1)
          ,CASE WHEN SUBSTRING(@s,r.Position+1,1)='"' THEN CASE WHEN r.QuoteIsOpen=0 THEN r.QuoteGroup+1 ELSE r.QuoteGroup END ELSE r.QuoteGroup END AS QuoteGroup
          ,CASE WHEN SUBSTRING(@s,r.Position+1,1)='"' THEN CASE WHEN r.QuoteIsOpen=0 THEN 1 ELSE 0 END ELSE r.QuoteIsOpen END AS QuoteIsOpen
    FROM recCTE r
    WHERE r.Position+1<=LEN(REPLACE(@s,' ','*')) 
)
SELECT r.QuoteGroup
      ,(
        SELECT CharAtPos AS [*]
        FROM recCTE r2 
        WHERE r2.QuoteGroup=r.QuoteGroup AND r2.QuoteIsOpen=1 AND r2.CharAtPos<>'"'
        ORDER BY r2.Position
        FOR XML PATH(''),TYPE).value('.','varchar(100)')
FROM recCTE r
WHERE r.QuoteGroup>=0
GROUP BY QuoteGroup;

一些解释:

递归CTE将逐字符遍历您的字符串。它将检查报价并跟踪处于打开状态处于关闭状态。依赖于此,所有带有 open 标志的值都将通过XML进行分组和重新连接。

答案 1 :(得分:1)

尝试一下;我将调试变量保留在输出表中

ALTER FUNCTION [dbo].[fn_SplitString] 
( 
    @string     NVARCHAR(MAX), 
    @delimiter  CHAR(1) 
) 
RETURNS @output TABLE
(
    [Id]        INT,
    [Start]     INT,
    [End]       INT,
    [Length]    INT,
    [Data]      NVARCHAR(MAX)
) 
BEGIN 
    DECLARE @count INT, @start INT, @end INT
    SELECT  @count = 1, @end = 0,
            @start = CHARINDEX(@delimiter, @string)
    WHILE   @start > 0 BEGIN 

        SELECT  @end = CHARINDEX(@delimiter, @string, @start + 1)

        INSERT INTO @output ([Id], [Start], [End], [Length], [Data])
        VALUES (@count, @start, @end, @end - @start - 1, 
                SUBSTRING(@string, @start + 1, @end - @start - 1)) 

        SELECT  @start = CHARINDEX(@delimiter, @string, @end + 1), 
                @count = @count + 1
    END 
    RETURN 
END

或者这基于接收两个不同定界符的想法:

ALTER FUNCTION [dbo].[fn_SplitString] 
( 
    @string     NVARCHAR(MAX), 
    @delimiter1 NVARCHAR(MAX),
    @delimiter2 NVARCHAR(MAX)
) 
RETURNS @output TABLE
(
    [Id]        INT,
    [Start]     INT,
    [End]       INT,
    [Length]    INT,
    [Data]      NVARCHAR(MAX)
) 
BEGIN 
    DECLARE @count INT, @start INT, @end INT
    SELECT  @count = 1, @end = 0
    SELECT  @start = CHARINDEX(@delimiter1, @string)
    SELECT  @end   = CHARINDEX(@delimiter2, @string, @start + 1)

    WHILE   @start > 0 AND @end > 0 BEGIN 

        INSERT INTO @output ([Id], [Start], [End], [Length], [Data])
        VALUES (@count, @start, @end, @end - @start - 1, 
                SUBSTRING(@string, @start + 1, @end - @start - 1)) 

        SELECT  @start = CHARINDEX(@delimiter1, @string, @end + 1)
        SELECT  @end   = CHARINDEX(@delimiter2, @string, @start + 1),
                @count = @count + 1
    END 
    RETURN 
END

答案 2 :(得分:1)

此修改版的Jeff Moden的splitter函数应为您提供所需的内容。这样可以避免循环,并且无需查找和替换某些字符串或字符。

CREATE FUNCTION dbo._SplitStrings
(
    @List NVARCHAR(MAX),
    @Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
    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
    ),
    E2(N) AS    ( SELECT 1 FROM E1 a, E1 b ),
    E4(N) AS    ( SELECT 1 FROM E2 a, E2 b ),
    E42(N) AS   ( SELECT 1 FROM E4 a, E2 b ),
    cteTally(N)  
    AS 
    (
        SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1))) 
                         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
       cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
                         WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0)),
  cteSplitByDelimiter AS
  (
      SELECT    s.N1, 
                SUBSTRING(@List, s.N1 - 1, 1) AS [StartDelimiter],
                SUBSTRING(@List , s.N1 + ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000), + 1) AS [EndDelimiter],
                SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000)) AS [Item]
        FROM cteStart s
  ),
  cteList AS
  (
      SELECT N1
            ,Item
            ,ROW_NUMBER() OVER (ORDER BY N1) % 2 AS ExpectedItem
      FROM  cteSplitByDelimiter sd
      WHERE sd.StartDelimiter = @Delimiter and sd.EndDelimiter = @Delimiter
  )
  SELECT    Item
  FROM      cteList 
  WHERE     ExpectedItem = 1;

答案 3 :(得分:0)

您需要使用replace()函数,然后将其传递给函数参数:

declare @string varchar(255) = '(("Name 1" and "Name 2") or "Name 3")'

set @string = replace(replace(replace(replace(replace(@string, '(', ''), ')', ''), '"', ''), 'and', ','), 'or', ',')

select *
from [dbo].[fn_SplitString](@string, ',') spt;

如果您使用的是最新版本的SQL Server,则可以使用TRANSLATE()代替replace()