Eva*_*oll 5 postgresql count string substring
如果我在这样的表中的行的列中有一个字符串
1 2 2 2 2 2 2
Run Code Online (Sandbox Code Playgroud)
我如何计算字符串2
中子字符串的出现次数。假设除了空格分隔符 之外没有其他任何内容" "
。
为此,我们将数字视为子字符串
CREATE TABLE foo
AS
SELECT 1 AS id, '1 2 2 2 2 2 2'::text AS data;
TABLE foo
id | data
----+---------------
1 | 1 2 2 2 2 2 2
Run Code Online (Sandbox Code Playgroud)
你可以用以下方法解决这个问题
FAST字符串函数,例如下面解释的模式之一
length(str) - length(*replace(str, replaceStr))
/ length(replaceStr)
Run Code Online (Sandbox Code Playgroud)length
和regexp_replace
大多数 RDBMS 提供了一些计算子字符串出现次数的方法,如下所示:
SELECT length(data) - length(replace(data, '2', ''))
/ length('2')
FROM foo;
Run Code Online (Sandbox Code Playgroud)
此方法在这里不适用,因为如果没有锚点,我们无法确定是否要替换以空格分隔的内容内的子字符串。例如,上面的内容替换了2
in 329
。我们可以通过使用regexp_replace
锚定子字符串来解决这个问题。
SELECT length(data) - length(regexp_replace(data, '\m2\M', '', 'g'))
/ length('2')
FROM foo;
Run Code Online (Sandbox Code Playgroud)
因为我们不是在简单的空格('')上进行分割,尽管我们可以更复杂地进行分割,所以我们可能还想容纳不同长度的子字符串,就像这个问题一样。这就是为什么我们明确包含/ length('2')
. 这减少了一项无操作,但如果我们要搜索的内容长于一个字符,那么它就是必需的。
SELECT length(data) - length(regexp_replace(data, '\m42\M', '', 'g'))
/ length('42')
FROM foo;
Run Code Online (Sandbox Code Playgroud)
ARRAY[]
在这里,我们必须减去一个匹配项,将字符串分割成两个片段,因此出现次数比片段计数少 1:对 , 的xyx
分割y
产生{'x', 'x'}
,并且我们希望长度与1
的出现次数相对应y
。
SELECT array_length(x, 1) - 1
FROM foo
CROSS JOIN LATERAL regexp_split_to_array(data, '\m2\M') AS t(x);
-- un-anchored version for reference.
-- CROSS JOIN LATERAL string_to_array(data, '2') AS t(x);
Run Code Online (Sandbox Code Playgroud)
或者,我们可以使用string_to_array
空格分隔的内容,然后计算匹配数,
SELECT id, array_length(array_positions(x, '2'), 1)
FROM foo
CROSS JOIN LATERAL string_to_array(data, ' ') AS t(x);
Run Code Online (Sandbox Code Playgroud)
regexp_split_to_table
在这里,我们将正则表达式拆分为一个表。在此方法中,您使用的是GROUP BY
and count()
。
SELECT id, x
FROM foo
CROSS JOIN LATERAL regexp_split_to_table(data, ' ')
AS t(x);
id | x
----+---
1 | 1
1 | 2
1 | 2
1 | 2
1 | 2
1 | 2
1 | 2
(7 rows)
Run Code Online (Sandbox Code Playgroud)
并且,您可以从那里运行常规 SQL。
SELECT id, x, count(*)
FROM (
SELECT id, x
FROM foo
CROSS JOIN LATERAL regexp_split_to_table(data, ' ')
AS t(x)
) AS t(id,x)
GROUP BY id, x;
id | x | count
----+---+-------
1 | 1 | 1
1 | 2 | 6
Run Code Online (Sandbox Code Playgroud)
regex_matches
在这里,我们摆脱了分割,而是使用\m
, 和\M
锚点作为单词边界。
SELECT count(*)
FROM foo
CROSS JOIN LATERAL regexp_matches(data, '\m2\M', 'g');
Run Code Online (Sandbox Code Playgroud)
事实证明,这种方法是总体上最快的,
CREATE LANGUAGE plperl
CREATE FUNCTION count_occurances(inputStr text, regex text)
RETURNS smallint
AS $BODY$
scalar @{[ $_[0] =~ m/$_[1]/g ]}
$BODY$
LANGUAGE plperl
IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)
遵循相同的数据格式,可以通过以下方式获得性能影响
CREATE TABLE foo
AS
SELECT
1 AS id,
array_to_string(
ARRAY(SELECT trunc(random()*100+1)::int % 100 FROM generate_series(1,5000) AS t(x)),
' '
) AS data
;
Run Code Online (Sandbox Code Playgroud)
在这些限制下,我发现使用 plperl 的过程方法是最快的。接下来我发现以下是最快的本机方法,
length(str) - regexp_replace(str, replacement, g)
/ length(replacement)
Run Code Online (Sandbox Code Playgroud)
请记住,除了需要锚定字符串之外,经过验证的真实字符串替换方法仍然是最快、最有效的本机方法,无论它可能有多笨拙,
length(str) - replace(str, replacement)
/ length(replacement)
Run Code Online (Sandbox Code Playgroud)
也就是说,该ARRAY[]
方法比拆分到表要快得多。
归档时间: |
|
查看次数: |
19487 次 |
最近记录: |