【问题标题】:Matching a substring against a stored list of strings将子字符串与存储的字符串列表匹配
【发布时间】:2021-11-30 08:05:54
【问题描述】:

因此,在最近的 Twitch 泄露事件之后,每个人都在讨论他们最喜欢的不良代码。突出的一点是a single monster SQL statement 发现“非法条款”。这让我开始思考如何“正确”实现这一点。

所以你有一个字符串表,你想匹配子字符串,你如何在 PL/pgSQL 中编写它?我假设任何 SQL 实现都应该具有为此类元编程创建函数/过程的过程能力,基本上创建和执行 SQL,如下所示:

admin@localhost:words> SELECT 'XabcY' LIKE '%abc%' OR 'XabcY' LIKE '%xyz%' as matches;
+-----------+
| matches   |
|-----------|
| True      |
+-----------+

所以更具体地说,给定表disallowed中的字符串列表:

| illegal_string |
|----------------|
| stupid         |
| witless        |
| moron          |
| commie-lover   |

如果其中任何一个与给定字符串匹配,您将如何在 PL/pgSQL 中创建一个动态查询,该查询在执行时返回 true?它不需要在 Twitch 中使用 ILIKE 来检查给定的单词是否包含子字符串,因此使用 position 也可以,但使用 gin 索引等应该是高性能/可调的。

Related issues.

【问题讨论】:

    标签: sql postgresql plpgsql


    【解决方案1】:

    有两种可能:

    1. 你可以使用LIKE ANY(array)操作符
    postgres=# select 'Ahoj' like any (ARRAY['Ah%', 'Na%']);
    ┌──────────┐
    │ ?column? │
    ╞══════════╡
    │ t        │
    └──────────┘
    (1 row)
    
    postgres=# select 'Nazdar' like any (ARRAY['Ah%', 'Na%']);
    ┌──────────┐
    │ ?column? │
    ╞══════════╡
    │ t        │
    └──────────┘
    (1 row)
    
    
    1. 你可以使用正则表达式:
    postgres=# select 'Ahoj' ~ '^(Ah|Na)';
    ┌──────────┐
    │ ?column? │
    ╞══════════╡
    │ t        │
    └──────────┘
    (1 row)
    
    postgres=# select 'Nazdar' ~ '^(Ah|Na)';
    ┌──────────┐
    │ ?column? │
    ╞══════════╡
    │ t        │
    └──────────┘
    (1 row)
    

    所以最终你不需要动态 SQL。

    还有 ANSI/SQL 语法:

    postgres=# select 'Nazdar' similar to '(Ah|Na)%';
    ┌──────────┐
    │ ?column? │
    ╞══════════╡
    │ t        │
    └──────────┘
    (1 row)
    

    所以你可以这样写:

    DECLARE pw text[];
    BEGIN
      pw := (SELECT array_agg('%' || disallowed || '%'
                FROM disallowed);
      IF EXISTS(SELECT * FROM foo WHERE c LIKE ANY (pw)) THEN
        RAISE NOTICE 'there are some disallowed words';
      END IF;
      ...
    

    我不确定性能。在较大的表上,您需要三元索引,或者最好使用全文而不是子字符串搜索。

    【讨论】:

    • > 所以最终你不需要动态SQL。不确定我明白你的意思。如何在表格的第二个选项中创建正则表达式?正则表达式需要从某种 SELECT 构建。关于第一个选项,我假设ARRAY['Ah%', 'Na%']); 可以从SELECT 上的illegal_words 创建。不知道我明白最后一个程序功能是如何工作的。 disallowed 指的是什么?
    • @oligofren - 这个例子中有一个错误。我修好了它。你可以在你的表上创建全文索引,同样你可以编写一个查询来搜索一些破碎的单词。 postgresql.org/docs/current/textsearch.html
    • 可能是这样,但您能否将第二版扩展为完整示例?我不明白如何通过选择illegal_words 来构造select 'Ahoj' ~ '^(Ah|Na)',这基本上是原始问题:)
    • @oligofren - 你可以使用子选择 select 'Ahoj' ~ (select string_agg(ilegal_words, '|') from disallowed) from ...
    【解决方案2】:

    我认为您不需要 PL/pgSQL 或动态 SQL。从不允许的单词表中创建一组文本,并作为正则表达式匹配设置的项目。使用正则表达式魔法可能不是很高效,但我希望直截了当。下面的查询当然可以参数化。

    select '<text to examine>' ~* 
      any(select illegal_string from disallowed) as rude;
    
    select 'You Stupido MORONE!' ~* 
      any(select illegal_string from disallowed) as rude;
    -- yields true.
    

    您可能希望仅搜索整个单词。然后使用正则表达式集并将其塑造成这样:

    select 'You Stupido MORONE!' ~* 
      any(select '\m'||illegal_string||'\M' from disallowed) as rude;
    -- yields false
    

    SQL 小提琴here

    【讨论】:

      猜你喜欢
      • 2013-06-18
      • 2023-03-28
      • 1970-01-01
      • 2013-03-07
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 2014-07-12
      相关资源
      最近更新 更多