这可以在单个 SQL 语句中完成。 (不幸的是,这种方法不满足使用正则表达式的要求。编辑:请参阅下面的后续内容,了解使用正则表达式的不优雅和低效的方法。)
我们可以获取A 列中字符“a”的出现次数,并将B 列设置为计数,查询如下:
UPDATE mytable t
SET t.B = ( CHAR_LENGTH(t.A) - CHAR_LENGTH(REPLACE(t.A,'a','')) )
让我们稍微解开一下。这是一个UPDATE 语句,没有WHERE 子句,所以我们将访问并可能更新mytable 中的每一行。 (我们正在分配一个表别名 t。这不是必需的,但我们将使用该别名限定我们以后对列 A 和 B 的引用,以使阅读 SQL 语句的人更清楚那些是对表中列的引用。
在下一行,我们有一个简单的SET 子句,为列B 赋值。
这是下一个表达式,我们分配给列 B 的值,我们应该稍微解压。
要计算'a' 字符的数量,我们可以使用一个小技巧:我们知道'a' 字符的长度正好是一个字符。
“诀窍”是使用REPLACE 函数,搜索所有出现的字符'a',并删除它们(用零长度字符串替换它们)。然后我们可以比较两个字符串的长度(字符数)。区别在于原始字符串中'a' 字符的数量。
作为这些表达式如何工作的演示:
SELECT t.foo AS foo
, REPLACE(t.foo,'a','') AS foo_2
, CHAR_LENGTH(t.foo) AS len
, CHAR_LENGTH(REPLACE(t.foo,'a','')) AS len_2
, CHAR_LENGTH(t.foo) - CHAR_LENGTH(REPLACE(t.foo,'a','')) AS `len-len_2`
FROM ( SELECT 'a b a a' AS foo
UNION ALL SELECT 'b c a'
UNION ALL SELECT 'b c d'
UNION ALL SELECT 'a '
) t
从那个查询返回:
foo foo_2 len len_2 len-len_2
------- ------ ------ ------ ---------
a b a a b 7 4 3
b c a b c 5 4 1
b c d b c d 5 5 0
a 3 2 1
注意:返回的内容基本上是对已删除字符数的计数。因此,如果我们想计算一个包含多个字符的字符串的出现次数,例如:cat,我们需要考虑这一点。
将返回值除以cat 中的字符数是一种方法。或者,我们可以将字符串 cat 替换为长度为两个字符的字符串,例如'xx',因此每次出现一个字符的长度差异。
跟进
最初的问题询问如何使用正则表达式计算“a”字符。我的第一个想法是 MySQL REGEXP 不可能,因为它的返回值要么是 NULL,要么是 0,要么是 1。但是稍微考虑一下,如果我们数到某个有限的数字,它是可以做到的的发生。可以检查一个字符串是否至少包含一个 'a' 字符,这非常简单:
'a b a a' REGEXP 'a'
如果匹配则返回 1,否则返回 0。还可以检查字符串是否包含至少两个“a”字符。这也很简单:
'a b a a' REGEXP 'a.*a'
如果我们将上面两个表达式的结果相加,我们可以得到 0、1 或 2 的“a”字符数。
我们可以重复相同的模式,将其扩展到匹配 3、4、5 等 'a' 字符。
这并不优雅,我们当然不想知道 CPU 在进行所有这些比较时会变得多热。但它确实返回指定的结果,最多为某个有限的最大计数。在这个例子中,六个。 (包含超过 6 个 'a' 字符的字符串将返回 6。
作为演示:
SELECT t.foo
, (t.foo REGEXP CONCAT('.*',REPEAT('a.*',1)))
+ (t.foo REGEXP CONCAT('.*',REPEAT('a.*',2)))
+ (t.foo REGEXP CONCAT('.*',REPEAT('a.*',3)))
+ (t.foo REGEXP CONCAT('.*',REPEAT('a.*',4)))
+ (t.foo REGEXP CONCAT('.*',REPEAT('a.*',5)))
+ (t.foo REGEXP CONCAT('.*',REPEAT('a.*',6)))
AS cnt_a
FROM ( SELECT 'a b a a' AS foo
UNION ALL SELECT 'b c a'
UNION ALL SELECT 'b c d'
UNION ALL SELECT 'a '
) t