字段中的字数统计(所有且唯一) - 有没有更优雅/最佳的方式?

Vér*_*ace 5 postgresql optimization string-manipulation query-performance

回答这个问题

鉴于此表(根据问题构建):

CREATE TABLE wordcount (id SERIAL NOT NULL PRIMARY KEY, description TEXT NOT NULL);

INSERT INTO wordcount (description) VALUES ('What a great day');
INSERT INTO wordcount (description) VALUES ('This is a product. It is useful');
Run Code Online (Sandbox Code Playgroud)

产生这个结果:

     id  | word_count | unique_word_count |  Description                        
---------+------------+-------------------+---------------
     1   |  4         | 4                 | What a great day
     2   |  7         | 6                 | This is a product. It is useful
Run Code Online (Sandbox Code Playgroud)

我给出了(正确的)答案,你可以在这里找到。

然而,在评论中,OP 然后又问了一个问题——如果有问题的字符串是['a', ' ', ' ', 'b']并且我的解决方案完全崩溃了怎么办 ——对于初学者来说,字符串甚至不会INSERT进入表格。

所以,现在的问题是,如何处理这样的字符串——即带有撇号、方括号等。我将给出我自己的答案,并为更优雅的解决方案提供奖励。

具有多种选择的解决方案将受到高度重视,那些显示出“跳出框框思考”证据的解决方案也将受到高度重视(抱歉陈词滥调 - 但它适合这里!:-))。我还将详细解释我的推理 - 这也将获得荣誉!提及其他服务器的选项也将获得优势。显然,我只能将奖金奖励给一个人,但我会赞成所有体面的答案。

我只能在两天内提供奖金 - 所以我会发布我的答案,并在我被允许时提供奖金 (+100)。此外,任何处理我自己无法处理的字符串的解决方案 - 还没有经过详尽的测试。

Erw*_*ter 3

至于您的解决方案:聪明且有可靠的解释。但是这些情况呢:\'\'NULL\'"\xc2\xa7$%\'\'-\'?没有言语。计数应该是0- 但您的解决方案完全删除了这些行。

\n\n

此外,任何解决方案首先取决于“单词”的确切定义,该定义可能会有很大差异......

\n\n

基于正则表达式的字符串处理

\n\n

与您的解决方案类似,有一些替代建议:

\n\n
SELECT id\n     , COALESCE(cardinality(arr), 0) AS word_count\n     , unique_word_count\n     , description\nFROM  (\n   SELECT *\n        , string_to_array(trim(regexp_replace(description, \'\\W+\', \' \', \'g\')), \' \') AS arr\n   FROM   wordcount\n   ) a\nLEFT   JOIN LATERAL (\n   SELECT count(DISTINCT elem) AS unique_word_count\n   FROM   unnest(arr) elem\n   ) b ON true;\n
Run Code Online (Sandbox Code Playgroud)\n\n

db<>fiddle here(扩展测试用例)

\n\n

核心是regexp_replace(description, \'\\W+\', \' \', \'g\')将所有非单词字符的子串替换为单个空格。请参阅正则表达式类简写转义。这会消除游戏早期的所有噪音。

\n\n

接下来是廉价trim()删除前导/尾随空格,并将string_to_array()准备好的字符串转换为数组。

\n\n

word_count直接从数组中获取。再说一遍:便宜。

\n\n

unique_word_count来自带有 的子LATERAL查询count(DISTINCT ...)。该部分可能会或可能不会慢于完全解除嵌套/聚合。有点简单。

\n\n

外部负责输入COALESCE(原始问题没有提到约束)。可选,以防您需要而不是.SELECTNULLNOT NULL0NULL

\n\n

或者(在使用短字符串的快速测试中更快):

\n\n
SELECT id\n     , count(*) AS word_count\n     , count(DISTINCT elem) AS unique_word_count\n     , description\nFROM  (\n   SELECT id, description\n        , unnest(string_to_array(trim(regexp_replace(description, \'\\W+\', \' \', \'g\')), \' \')) AS elem\n   FROM   wordcount\n   ) sub\nGROUP  BY id, description;\n
Run Code Online (Sandbox Code Playgroud)\n\n

这会像您的答案一样删除包含 0 个单词的行。

\n\n

(Ab-)使用文本搜索解析器

\n\n

使用文本搜索功能ts_parse()更简单。可能会更快,也可能不会更快。但首先研究文本搜索解析器识别的各种标记,看看哪些符合您对“单词”的定义:

\n\n
SELECT * FROM ts_token_type(\'default\')\n
Run Code Online (Sandbox Code Playgroud)\n\n

仅适用于“ASCII Words”:\n(与上面不同,这里下划线 ( _) 不被视为单词字符):

\n\n
SELECT w.id\n     , count(*) AS word_count\n     , count(DISTINCT token) AS unique_word_count\n     , w.description\nFROM   wordcount w, ts_parse(\'default\', w.description) t\nWHERE  t.tokid = 1 -- \'asciiword\'\nGROUP  BY w.id;\n
Run Code Online (Sandbox Code Playgroud)\n\n

为了避免_分隔单词,请replace()首先使用 simple:

\n\n
SELECT w.id\n     , count(*) AS word_count\n     , count(DISTINCT token) AS unique_word_count\n     , w.description\nFROM   wordcount w, ts_parse(\'default\', replace(w.description, \'_\', \'x\')) t\nWHERE  t.tokid = 1 -- \'asciiword\'\nGROUP  BY w.id;\n
Run Code Online (Sandbox Code Playgroud)\n\n

再次,保留所有行:

\n\n
SELECT w.id\n     , count(token) AS word_count\n     , count(DISTINCT token) AS unique_word_count\n     , w.description\nFROM   wordcount w\nLEFT   JOIN LATERAL (\n   SELECT t.token\n   FROM   ts_parse(\'default\', w.description) t\n   WHERE  t.tokid = 1 -- \'asciiword\'\n   ) t ON true\nGROUP  BY w.id;\n
Run Code Online (Sandbox Code Playgroud)\n\n

db<>在这里摆弄

\n\n

有关的:

\n\n\n