假设我在 Postgres 中有一个具有此值的文本字段:
'bar$foo$john$doe$xxx'
我想将最后一次出现的美元 ( $
) 字符替换为另一个字符,例如“-”。替换后,该字段的内容应为:
'bar$foo$john$doe-xxx'
假设我在 Postgres 中有一个具有此值的文本字段:
'bar$foo$john$doe$xxx'
我想将最后一次出现的美元 ( $
) 字符替换为另一个字符,例如“-”。替换后,该字段的内容应为:
'bar$foo$john$doe-xxx'
介绍:
这个问题涉及到一些横向思考。任何字符的最后一次出现也是该字符在反转时字符串中的第一次出现!所有的解决方案(一栏)都使用这种方法。
对于提出的所有 5 个解决方案,我们有以下内容(以下所有代码的小提琴都可以在此处获得。每个解决方案的单独小提琴包含在下面的每个解决方案中):
PRIMARY KEY
只有第 5 个解决方案需要。然后我们运行以下查询来填充表——第一条记录是 OP 自己的数据——其余的都是随机生成的!操作:
随机数据:
解决方案将按性能排序。它在 db<>fiddle 和家庭虚拟机(16GB RAM,SSD)上进行了测试,测试表中有 10M 记录 - 不要在 fiddle 上尝试 10M!每种方法都给出了一个因素,即它比 VM 上最快的方法花费了多长时间。
在所有情况下,
bar$foo$john$doe-xxx
对于 OP 的原始数据和测试查询(LIMIT 2
显示它们的行为符合预期 - 即用$
连字符 ( ) 替换最后一个美元 ( ) 符号) ,都可以获得所需的结果-
。您可以在小提琴检查。1:Postgresql 字符串函数(参见手册)、使用
OVERLAY()
、STRPOS()
ANDREVERSE()
(个人小提琴):2: Reverse() 和正则表达式函数REGEXP_REPLACE()(个人小提琴):
正在做的事情(从内到外)是:
REVERSE()
字符串,REGEXP_REPLACE('xxx', '\$', '-')
在反转的字符串上运行。请注意,这只会替换第一个实例,
$
因为'g'
(global) 标志不存在 - 如果代码 read... , '-', 'g')
,那么所有美元都将被替换 - 无论如何你都可以使用(便宜得多)REPLACE()
函数来做到这一点。另请注意,这
$
是一个正则表达式meta-character
- 即它在正则表达式中具有特殊功能(它表示字符串的最后一个字符),因此\
在替换它时必须使用反斜杠 ( ) 字符对其进行转义。然后,最后一步是将我们编辑的字符串反转回原来的顺序,我们就有了结果!
值得记住的是,正则表达式非常强大。不幸的是(释义),强大的力量带来了巨大的复杂性。正则表达式很容易变得复杂且难以理解——但它们非常值得一试——它们可以将代码页面变成专家手中的单行代码!
首先尝试使用非正则表达式功能找到不同的解决方案总是值得的(参见解决方案 1),但它们有自己的位置,在这种情况下,它工作得相当好!上面链接的站点是开始探索它们的好地方。
3:使用 REGEXP_REPLACE() 的替代正则表达式(不使用 REVERSE() -请参阅 Evan Carroll 的回答(个人小提琴)):
性能:10M 记录的时间 = 16316.768 ms。
与最快的比较 = 2.03 x
4:仅替代字符串函数,使用
SUBSTRING()
,POSITION()
和LENGTH()
(单独的小提琴):5:(
ARRAY
手动) -v.慢但演示STRING_TO_ARRAY()
,UNNEST()
和1 (个人小提琴)WITH ORDINALITY
1:参见Erwin Brandstetter 的这些帖子(1、2和3 )
WITH ORDINALITY
个人小提琴展示了许多方法以及性能分析和一些讨论。仅出于完整性考虑而包含在内,在这种情况下,不作为一个现实的选择。
尽管在这种特殊情况下,该
ARRAY
技术的性能不是很高(由于具有子查询),但服务器的许多后端代码都使用ARRAY
s,它们通常是解决各种问题的最佳方法。了解 PostgreSQL 这个鲜为人知的角落是非常值得的。首先要做的是:
结果(OP的记录-
xxx
由于REVERSE(),注释首先出现):字符串按字符拆分为字段
$
。然后:
结果:
我们需要 the 的原因
WITH ORDINALITY
是没有 is,我们无法区分字符串的第一个元素(即我们感兴趣的元素)和其他元素(elem, num)
;然后,我们这样做:
结果:
这样做是为了将反转的字符串聚合回其原始形式,使用
$
作为分隔符,但不包括第一个元素 (WHERE num > 1;
)。代替第一个元素的是第一个元素 - 数组引用[1]
+ 连字符 (|| '-' ||
) 等等,我们xxx-
加上反向字符串的其他元素,并将$
它们分开。然后,我们只是简单地应用于
REVERSE()
整个构造以给出所需的结果!有一个解决方案可能不使用
WITH ORDINALITY
(ROW_NUMBER()代替) - 请参阅个人小提琴中的讨论。表现
每个查询都会显示家庭 VM 上 1000 万条记录的性能数据 - db<>fiddle(30,000 条记录)结果在相对量级方面相当接近地反映了它们。
因此,在这种情况下,尽可能使用基于字符串的方法,但是正则表达式可以帮助减少SLOC计数,但是它们可能会更慢 - 由 DBA/Dev 在速度和复杂性之间做出选择。
使用
regexp_replace
您可以使用正则表达式和捕获括号来执行此操作
regexp_replace
将最后一个替换
$
为-
. 最终结果是,下面是它的工作原理,
$
并\1
保存。$
然后它恢复捕获
\1
并添加一个-
保留字符串的其余部分。为您提供另一个选择。
诚然,这可能与您的情况相关,也可能不相关,但无论如何我都会提到它,如果只是为了完整性......
正确规范您的数据,使每个字段只有一个值。
这样,改变任何一个值就变成了 Childs-Play。这只是一个定期更新声明。
警告:如果您从不使用该字段值的任何部分查询您的表,那么这种方法 [可能] 过大。