在许多编程语言中,您可以使用>,> =,<等运算符来比较字符串。等等......语言将基于字母表中字母的位置进行比较。
例如在PHP中
if ('a' < 'b') {
echo 'Yes';
} else {
echo 'No';
}
> Yes
但是在postgres或mysql中
SELECT
CASE WHEN 'a' < 'b' THEN 'yes' END
FROM table
Output: null
我有一个包含字符串的表,我需要通过SQL进行相互比较。
例如: 6.2(5a)中 6.2(5b) - 这将大于6.2(5a) 要么 6.2(15) - 这将大于6.2(5a)
我想过使用正则表达式为一个字母分配一个数字,但是当没有字母时,这会打破比较。
你将如何在SQL中完全解决这个问题?
答案 0 :(得分:11)
大多数字符串比较默认为所谓的ASCIIbetical sorting。它将每个字符转换为其ASCII编号(或者可能是UTF-8,它与前7位的ASCII重叠)然后进行排序。这个似乎可以很好地排序......
select 'a' < 'z'; -- true
......但很快就会出错。
select 'a' < 'Z'; -- false
这是因为所有大写字母都以ASCII和UTF-8中的所有小写字母结尾。
解决此问题的简单方法是通过双方调用upper
或lower
来规范化案例。
select lower('a') < lower('Z'); -- true
ASCIIbetical排序的第二个问题是数字。再次,它开始很好......
select 'a9' < 'a1'; -- true
......但很快就去了锅。
select 'a9' < 'a10'; -- false
ASCIIbetical排序一次比较一个字符。 ASCII 9(57)小于ASCII 1(49)。
你想要的是natural sort,其中字符串部分被比较为字符串,数字被比作数字。在SQL中进行自然排序并不是最简单的事情。您需要固定的字段宽度来分别对每个子字符串进行排序,或者可能需要使用正则表达式...
幸运的是pg_natural_sort_order,这是一个实现高效自然排序的Postgres扩展。
如果您无法安装扩展程序,则可以使用2kan等btrsort之类的存储过程。
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;
虽然它没有提供比较运算符,但我不会假装理解它。这允许您在order by
。
select * from things order by btrsort(whatever);
为防止自然排序的查询在大型表格上变为泥泞,you can create a btree index on the result of that function。
create index things_whatever_btrsort_idx ON things( btrsort(whatever) );