查找文本数组包含与输入相似的值的行

Find rows where text array contains value similar to input

我正在尝试获取类型为 text[] 的列包含类似于某些用户输入的值的行。

到目前为止我的想法和做法是像这样使用 'ANY''LIKE' 运算符:

select * from someTable where '%someInput%' LIKE ANY(someColum);

但是没用。查询 returns 与此查询的值相同:

select * from someTable where 'someInput' = ANY(someColum);

我在子查询中使用 unnest() 函数得到了很好的结果,但如果可能,我需要在 WHERE 子句中查询它。

为什么 运算符 LIKE 不能与 ANY 运算符一起使用并且我没有收到任何错误?我认为一个原因应该是 ANY 运算符在查询的右侧,但是 ...

有没有不使用 unnest() 的解决方案,如果可以在 WHERE 子句中解决?

了解 ANY 不是 运算符 而是一个 SQL 结构也很重要仅用于 right 运算符。更多:

LIKE 运算符 - 或更准确地说:表达式,重写为 ~~ 运算符 在 Postgres 内部 - 期望左侧的 value 和右侧的 pattern。这个运算符没有 COMMUTATOR(就像简单的相等运算符 = 一样)所以 Postgres 不能翻转操作数。

您的尝试:

select * from someTable where '%someInput%' LIKE ANY(someColum);

已左右翻转操作数,因此 '%someInput%' 并且数组列 someColum 的元素被视为模式(这不是你想要什么)。

必须是 ANY(someColum) LIKE '%someInput%' - 除了 ANY 构造是不可能的只允许操作员的 权限 。你在这里遇到了障碍。

相关:

  • Is there a way to usefully index a text column containing regex patterns?
  • Can PostgreSQL index array columns?

您可以规范化您的关系设计并将数组的 元素 保存在单独的 table 中的单独行中。除此之外,unnest() is the solution, as you already found yourself. But while you are only interested in the existence of at least one matching element, an EXISTS 子查询将是最有效的,同时避免结果中的重复 - Postgres 可以在找到第一个匹配项后立即停止搜索:

SELECT *
FROM   tbl
WHERE  EXISTS (
    SELECT -- can be empty 
    FROM   unnest(someColum) elem
    WHERE  elem LIKE '%someInput%'
  );

您可能想要转义 someInput 中的特殊字符。参见:

  • Escape function for regular expression or LIKE patterns

NULL 可能涉及时,请注意否定 (NOT LIKE ALL (...)):

我的 question 被标记为重复,并且被粗心的 mod 链接到一个断章取义的问题。这个问题最接近我问的问题所以我在这里留下我的答案。 (我认为这可能会帮助那些 unnest() 会成为解决方案的人)

在我的例子中,DISTINCTunnest() 的组合是解决方案:

SELECT DISTINCT ON (id_) *
FROM (
  SELECT unnest(tags) tag, *
  FROM someTable
  ) x
WHERE (tag like '%someInput%');

unnest(tags) 将文本数组扩展为行列表,DISTINCT ON (id_) 根据唯一的 id_ 列删除扩展产生的重复项。

更新

WHERE 子句中没有 DISTINCT 的另一种方法是:

SELECT *
FROM someTable 
WHERE (
  0 < (
    SELECT COUNT(*) 
    FROM unnest(tags) AS tag
    WHERE tag LIKE '%someInput%'
  )
);

一个公认的不完美的可能性可能是使用 ARRAY_TO_STRING,然后对结果使用 LIKE。例如:

SELECT *
FROM someTable
WHERE ARRAY_TO_STRING(someColum, '||') LIKE '%someInput%';

这种方法可能存在问题,因为如果有人发现连接字符序列,他们可能会搜索两个数组元素。例如,如果用户输入 i||M 代替 someInput,则与 || 连接的 {'Hi','Mom'} 数组将 return 结果。相反,预期可能是在这种情况下不会有结果,因为 HiMom 都不单独包含 i||M 字符序列。

.

这个答案正是我要找的。它还提供了一些有用的提示(和示例),以备您需要更多灵活性时使用。

它基本上解释了 ANY()@>&& 运算符.

"If you want to search multiple values, you can use @> operator"

"@> 表示包含该数组中的所有值。如果要搜索当前数组是否包含另一个数组中的任何值,可以使用 &&"