使用比较运算符比较postgres中的字符串?

bak*_*ike 6 postgresql natural-sort

在许多编程语言中,您可以使用>,> =,<etc等运算符来比较字符串,并且语言将基于字母表中字母位置的比较.

例如在PHP中

if ('a' < 'b') {
    echo 'Yes';
} else {
    echo 'No';
}
> Yes
Run Code Online (Sandbox Code Playgroud)

但是在postgres或mysql中

SELECT
CASE WHEN 'a' < 'b' THEN 'yes' END
FROM table
Output: null
Run Code Online (Sandbox Code Playgroud)

我有一个包含字符串的表,我需要通过SQL相互比较.

例如:6.2(5a)6.2(5b) - 这将大于6.2(5a)或6.2(15) - 这将大于6.2(5a)

我想过使用正则表达式给一个字母分配一个数字但是当没有字母时这会打破比较.

你会如何在SQL中纯粹地解决这个问题?

Sch*_*ern 12

大多数字符串比较默认为所谓的ASCIIbetical排序.它将每个字符转换为其ASCII编号(或者可能是UTF-8,它与前7位的ASCII重叠)然后进行排序.这看起来很好......

select 'a' < 'z'; -- true
Run Code Online (Sandbox Code Playgroud)

......但很快就会出错.

select 'a' < 'Z'; -- false
Run Code Online (Sandbox Code Playgroud)

这是因为所有大写字母都以ASCII和UTF-8中的所有小写​​字母结尾.

解决这个问题的简单方法是通过调用upperlower双方来规范化大小写.

select lower('a') < lower('Z'); -- true
Run Code Online (Sandbox Code Playgroud)

ASCIIbetical排序的第二个问题是数字.再次,它开始很好......

select 'a9' < 'a1'; -- true
Run Code Online (Sandbox Code Playgroud)

......但很快就去了锅.

select 'a9' < 'a10'; -- false
Run Code Online (Sandbox Code Playgroud)

ASCIIbetical排序一次比较一个字符.ASCII 9(57)小于ASCII 1(49).


你想要的是一种自然的排序,其中字符串部分作为字符串进行比较,数字作为数字进行比较.在SQL中进行自然排序并不是最简单的事情.您需要固定的字段宽度来分别对每个子字符串进行排序,或者可能需要使用正则表达式...

幸运的是有pg_natural_sort_order,一个实现高效自然排序的Postgres扩展.

如果您无法安装扩展,则可以使用像bkrsort by 2kan 这样的存储过程.

CREATE FUNCTION btrsort_nextunit(text) RETURNS text AS $$
    SELECT 
        CASE WHEN $1 ~ '^[^0-9]+' THEN
            COALESCE( SUBSTR( $1, LENGTH(SUBSTRING($1 FROM '[^0-9]+'))+1 ), '' )
        ELSE
            COALESCE( SUBSTR( $1, LENGTH(SUBSTRING($1 FROM '[0-9]+'))+1 ), '' )
        END

$$ LANGUAGE SQL;

CREATE FUNCTION btrsort(text) RETURNS text AS $$
    SELECT 
        CASE WHEN char_length($1)>0 THEN
            CASE WHEN $1 ~ '^[^0-9]+' THEN
                RPAD(SUBSTR(COALESCE(SUBSTRING($1 FROM '^[^0-9]+'), ''), 1, 12), 12, ' ') || btrsort(btrsort_nextunit($1))
            ELSE
                LPAD(SUBSTR(COALESCE(SUBSTRING($1 FROM '^[0-9]+'), ''), 1, 12), 12, ' ') || btrsort(btrsort_nextunit($1))
            END
        ELSE
            $1
        END
      ;
$$ LANGUAGE SQL;
Run Code Online (Sandbox Code Playgroud)

虽然它没有提供比较运算符,但我不会假装理解它.这允许你在一个中使用它order by.

select * from things order by btrsort(whatever);
Run Code Online (Sandbox Code Playgroud)

为了防止自然排序的查询在大型表上变为泥,您可以在该函数的结果上创建btree索引.

create index things_whatever_btrsort_idx ON things( btrsort(whatever) );
Run Code Online (Sandbox Code Playgroud)