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)。此外,任何处理我自己无法处理的字符串的解决方案 - 还没有经过详尽的测试。
至于您的解决方案:聪明且有可靠的解释。但是这些情况呢:\'\'
、NULL
、\'"\xc2\xa7$%\'
、\'-\'
?没有言语。计数应该是0
- 但您的解决方案完全删除了这些行。
此外,任何解决方案首先取决于“单词”的确切定义,该定义可能会有很大差异......
\n\n与您的解决方案类似,有一些替代建议:
\n\nSELECT 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\ndb<>fiddle here(扩展测试用例)
\n\n核心是regexp_replace(description, \'\\W+\', \' \', \'g\')
将所有非单词字符的子串替换为单个空格。请参阅正则表达式类简写转义。这会消除游戏早期的所有噪音。
接下来是廉价trim()
删除前导/尾随空格,并将string_to_array()
准备好的字符串转换为数组。
word_count
直接从数组中获取。再说一遍:便宜。
unique_word_count
来自带有 的子LATERAL
查询count(DISTINCT ...)
。该部分可能会或可能不会慢于完全解除嵌套/聚合。有点简单。
外部负责输入COALESCE
(原始问题没有提到约束)。可选,以防您需要而不是.SELECT
NULL
NOT NULL
0
NULL
或者(在使用短字符串的快速测试中更快):
\n\nSELECT 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使用文本搜索功能ts_parse()
更简单。可能会更快,也可能不会更快。但首先研究文本搜索解析器识别的各种标记,看看哪些符合您对“单词”的定义:
SELECT * FROM ts_token_type(\'default\')\n
Run Code Online (Sandbox Code Playgroud)\n\n仅适用于“ASCII Words”:\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:
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\nSELECT 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\ndb<>在这里摆弄
\n\n有关的:
\n\n\n