【问题标题】:Escaping ] and ^ characters in a T-SQL "pattern" expression character class在 T-SQL“模式”表达式字符类中转义 ] 和 ^ 字符
【发布时间】:2013-03-08 09:32:43
【问题描述】:

我正在尝试使用以下查询在 MsSql Server 2008 R2 中模拟 Oracle 的 RTRIM(expression, characters)

REVERSE(
        SUBSTRING(
                  REVERSE(field),
                  PATINDEX('%[^chars]%', REVERSE(field)),
                  LEN(field) - PATINDEX('%[^chars]%', REVERSE(field)) + 1
             )
       )

问题是我希望能够修剪像 ]^ 这样可能需要转义的字符。

我不知道该怎么做。 \] 之类的东西不起作用。

我知道ESCAPE 子句,但我不明白它是如何工作的,顺便说一句,如果放在模式字符串之后,SqlServer 会拒绝它。

有趣的事实:

如果我写%[^^]%(想要修剪^),它就行不通了。

如果我写%[^ ^]%,它会修剪^,但显然也会修剪空格!

【问题讨论】:

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


【解决方案1】:

不漂亮,但是……

CREATE FUNCTION dbo.RTRIMCHARS(
    @input AS VARCHAR(MAX), @chars AS VARCHAR(100)
) RETURNS VARCHAR(MAX) 
AS 
BEGIN
    DECLARE @charpos BIGINT
    DECLARE @strpos BIGINT

    SET @strpos = LEN(@input)
    SET @charpos = LEN(@chars)

    IF @strpos IS NULL OR @charpos IS NULL RETURN NULL
    IF @strpos = 0 OR @charpos = 0 RETURN @input

    WHILE @strpos > 0
    BEGIN
        SET @charpos = LEN(@chars)
        WHILE @charpos > 0
        BEGIN
            IF SUBSTRING(@chars, @charpos, 1) = SUBSTRING(@input, @strpos, 1)
            BEGIN
                SET @strpos = @strpos - 1
                BREAK
            END
            ELSE
            BEGIN
                SET @charpos = @charpos - 1
            END
        END
        IF @charpos = 0 BREAK
    END
    RETURN SUBSTRING(@input, 1, @strpos)
END

用法

SELECT dbo.RTRIMCHARS('bla%123', '123%')   -- 'bla'
SELECT dbo.RTRIMCHARS('bla%123', '123')    -- 'bla%'
SELECT dbo.RTRIMCHARS('bla%123', 'xyz')    -- 'bla%123'
SELECT dbo.RTRIMCHARS('bla%123', ']')      -- 'bla%123'
SELECT dbo.RTRIMCHARS('bla%123', '')       -- 'bla%123'
SELECT dbo.RTRIMCHARS('bla%123', NULL)     -- NULL
SELECT dbo.RTRIMCHARS(NULL, '123')         -- NULL

【讨论】:

  • 感谢您的回答,但我自己写完了。看看吧:)
  • 你的函数似乎不能正常工作:如果我调用 RTRIMCHARS('[PROJECT]', 'E') 它返回 [PROJ
  • @Teejay 我已经修复了我的代码以按照规范工作。唉,它现在也使用嵌套循环。 ;-) 我会投票支持你提出自己的解决方案;我只是不想让损坏的代码出现在我的答案中。如果您愿意,请针对您的功能进行基准测试,我会对您的发现非常感兴趣!
  • 我发现原生 LTRIM(str)(如我的)或原生 RTRIM(str)(如你的)运行速度比 REVERSE(LTRIM(REVERSE(str))) 快约 20%,尽管后者在代码可维护性方面有所提高,因为您只需要编辑一个函数。
  • 附带说明,我在 PLSQL (Oracle) 中实现了相同的功能,它的响应时间与股票 LTRIM 相似。基本上,即使是原生函数也需要做同样的操作。
【解决方案2】:

我在 MS Connect 上找到了这份文档:
http://connect.microsoft.com/SQLServer/feedback/details/259534/patindex-missing-escape-clause

用户使用PATINDEX 询问ESCAPE 子句,然后另一个用户也扩展了对CHARINDEX 的请求。

MS 回答:Ticket 已关闭,因为 无法修复 :(

我完成了为LTrim 编写自己的自定义函数:

CREATE FUNCTION LTrim_Chars (
  @BaseString varchar(2000),
  @TrimChars varchar(100)
)

RETURNS varchar(2000) AS

BEGIN

  DECLARE @TrimCharFound bit

  DECLARE @BaseStringPos int
  DECLARE @TrimCharsPos int

  DECLARE @BaseStringLen int
  DECLARE @TrimCharsLen int

  IF @BaseString IS NULL OR @TrimChars IS NULL
  BEGIN
      RETURN NULL
  END

  SET @BaseStringPos = 1

  SET @BaseStringLen = LEN(@BaseString)
  SET @TrimCharsLen = LEN(@TrimChars)

  WHILE @BaseStringPos <= @BaseStringLen
  BEGIN 

      SET @TrimCharFound = 0
      SET @TrimCharsPos = 1

      WHILE @TrimCharsPos <= @TrimCharsLen
      BEGIN     
          IF SUBSTRING(@BaseString, @BaseStringPos, 1) = SUBSTRING(@TrimChars, @TrimCharsPos, 1)
          BEGIN
              SET @TrimCharFound = 1
              BREAK
          END             
          SET @TrimCharsPos = @TrimCharsPos + 1     
      END

      IF @TrimCharFound = 0
      BEGIN
        RETURN SUBSTRING(@BaseString, @BaseStringPos, @BaseStringLen - @BaseStringPos + 1)
      END       
      SET @BaseStringPos = @BaseStringPos + 1

  END

  RETURN ''

END

对于RTrim

CREATE FUNCTION RTrim_Chars (
  @BaseString varchar(2000),
  @TrimChars varchar(100)
)

RETURNS varchar(2000) AS

BEGIN

  RETURN REVERSE(LTrim_Chars(REVERSE(@BaseString), @TrimChars))

END

至少,我学会了一些MsSql脚本……


编辑:

我添加了NULL 检查这两个参数,以反映 Oracle 和 Postgres 的行为。

不幸的是,Oracle 的行为仍然略有不同:
在你写LTRIM(string, '')的情况下,它返回NULL,因为0长度的字符串就像Oracle中的NULL,所以它实际上返回LTRIM(string, NULL)的结果,确实是NULL

顺便说一句,这是一个非常奇怪的案例。

【讨论】:

  • WHILE 循环中逐个字符地遍历输入(还有一个嵌套循环!)可能比我的建议效率低。
  • 通常情况下,我会修剪 1-2 个字符,因此性能下降应该是个问题。无论如何,我会更好地检查你的功能!谢谢
  • 另请注意,我的解决方案支持a-z0-9 中的字符范围。这可能会派上用场。
  • 是的,我明白了。但它有一些基本问题......请参阅我对您的回答的评论
  • 是的,我明白这一点。事实上,范围支持是我使用PATINDEX 的副作用,所以这并不是故意的。
猜你喜欢
  • 1970-01-01
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-05
相关资源
最近更新 更多