PG迁移必读(三):复现Sybase的Patindex函数
Barry Chen

前言:本文介绍了如何在PostgreSQL中实现类似Sybase的patindex函数,旨在帮助开发者解决PG迁移中遇到的函数兼容性问题。

客户系统中有不少地方使用了Patindex函数,该函数支持简单的通配符,返回子字符串位于查找字符串的开始位置。以下是Sybase的官方文档描述:


PATINDEX ( '%pattern%', string-expression )

Returns the starting position of the first occurrence of a specified pattern.

 

pattern The pattern for which you are searching. This string is limited to 126 bytes for patterns with wildcards. If the leading percent wildcard is omitted, PATINDEX returns one (1) if the pattern occurs at the beginning of the string, and zero if not. If pattern starts with a percent wildcard, then the two leading percent wildcards are treated as one.

其中参数pattern支持的通配符如下表:

Wildcard

Matches

_ (underscore)

Any one character

% (percent)

Any string of zero or more characters

[]

Any single character in the specified range or set

[^]

Any single character not in the specified range or set

为了解Patindex函数具体用法,让大家有个感性认识,先看几个例子:

-- 返回2,因‘%hoco%’匹配整个字符串,hoco从第2个位置开始

SELECT PATINDEX( '%hoco%', 'chocolate' )  

 

-- 返回11,因‘%4_5_’匹配整个字符串,4_5_从第11个位置开始

SELECT PATINDEX ('%4_5_', '0a1A 2a3A 4a5A' )

 

-- 返回0,因'0a1A 2a3A 4a5A'不从4开始:

SELECT PATINDEX ('4_5_', '0a1A 2a3A 4a5A' )

 

-- 返回0,因'abc-123-def'不以数字结束:

SELECT PATINDEX ('%[0-9]', 'abc-123-def'

 

-- 返回5,因'123'从第5个位置开始:

SELECT PATINDEX ('%[0-9]%', 'abc-123-def')  

 

-- 返回1,因'abc-123-def'第一个字符就不为数字:

SELECT PATINDEX ('%[^0-9]%', 'abc-123-def'

考虑到以上规则,以下是基于PLpgSQL的一个patindex函数实现版本:


CREATE OR REPLACE FUNCTION patindex( pattern VARCHAR, expression VARCHAR

RETURNS INT AS $$

DECLARE v_i int;

v_pattern varchar; 

BEGIN 

 

v_pattern:=TRIM( pattern, '%' );

IF left(pattern, 1)!='%' THEN

v_pattern:='^'||v_pattern;

END IF;

IF right(pattern, 1)!='%' THEN

v_pattern:= v_pattern||'$';

END IF;

 

SELECT COALESCE(

   STRPOS( $2

       ,(  SELECT

               ( REGEXP_MATCHES( $2

                   ,'(' || REPLACE( REPLACE( v_pattern, '%', '.*?' ), '_', '.' ) || ')'

               ) )[ 1 ]

           LIMIT 1

       )

   )

   ,0

) INTO v_i;  

RETURN v_i;

END;

$$ LANGUAGE plpgsql IMMUTABLE;

经测试,该函数与Sybase的返回结果一致。

 

感谢阅读,期待你的实践反馈!