Return 行,其中数组列与给定数组中的每个模式都匹配

Return rows where array column has match for every pattern in given array

我有以下 table:

╔════╦════════════════════════════════════════╗
║ id ║                 value                  ║
╠════╬════════════════════════════════════════╣
║  1 ║ ['friend', 'apple', 'cat']             ║
║  2 ║ ['cat', 'friend', 'dog']               ║
║  3 ║ ['pasta', 'best-friend', 'lizard']     ║
║  4 ║ ['wildcat', 'potato', 'alices-friend'] ║
╚════╩════════════════════════════════════════╝

我的目标是 return 所有 value 包含给定数组的行。例如:

  1. ['friend', 'cat'] 应该 return 行 12.

  2. ['%friend%', '%cat%'] 应该 return 行 124.

目前我正在使用这个命令:

SELECT DISTINCT(id), value
FROM table
WHERE value @> (ARRAY['friend', 'cat']::VARCHAR[]);

但是当 (array['%friend%', '%cat%']::varchar[]).

时,上面列出的示例 2 不起作用

因为它适用于示例 1,我认为问题出在 % 符号上,但我不知道如何处理这个问题,因为我不需要显式匹配这些值。

DBFiddle

您想要在数组列 value 中匹配给定数组中的 每个 LIKE 模式匹配项。

这个查询很棘手,主要原因有两个:

  1. 没有 array operator 可以将整个数组与 LIKE 模式的数组进行比较。 (没有带 pattern-matching 的“数组包含”运算符。)数组列必须未嵌套。

  2. 在取消嵌套后简单地计算匹配项是不够的,因为一个模式可以匹配多次,从而掩盖另一个匹配项的缺失。

像这样改写任务:

"Return 所有输入模式 none 的行都找不到匹配项。"

此查询尽可能高效地实现它:

SELECT t.id, t.value
FROM   tbl t
WHERE  NOT EXISTS (
   SELECT FROM unnest('{%friend%, %cat%}'::text[]) AS p(pattern)
   WHERE  NOT EXISTS (
      SELECT FROM unnest(t.value) AS a(elem)
      WHERE  a.elem LIKE p.pattern
      )
   );

db<>fiddle here

不幸的是,可能没有索引支持。您必须规范化您的关系设计以允许 - 使用 many-to-one table 替换数组 value.

旁白

无论哪种方式,为了优化性能,fork 两种不同的情况[​​=80=]:搜索有和没有特殊 LIKE 字符。只需检查具有特殊含义的字符是否存在,即 \%_ 之一。相关:

  • Escape function for regular expression or LIKE patterns

您的简单查询可以处理简单的相等性 - 在对其进行清理后:

SELECT id, value
FROM   tbl
WHERE  value @> '{friend, cat}';

DISTINCT(id), value 只是 DISTINCT id, value 的误导性等效语法变体。您是否将其与 DISTINCT ON 混淆了?参见:

  • Select first row in each GROUP BY group?

并且,假设 id 是 PK,那么 DISTINCT 只是给定查询中的昂贵 no-op。删除它。

最后,使用 text[] 而不是 varchar[]。在极端情况下,text[] 更优越,text"preferred" string type。参见: