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
包含给定数组的行。例如:
['friend', 'cat']
应该 return 行 1
和 2
.
['%friend%', '%cat%']
应该 return 行 1
、2
和 4
.
目前我正在使用这个命令:
SELECT DISTINCT(id), value
FROM table
WHERE value @> (ARRAY['friend', 'cat']::VARCHAR[]);
但是当 (array['%friend%', '%cat%']::varchar[])
.
时,上面列出的示例 2 不起作用
因为它适用于示例 1,我认为问题出在 %
符号上,但我不知道如何处理这个问题,因为我不需要显式匹配这些值。
您想要在数组列 value
中匹配给定数组中的 每个 LIKE
模式匹配项。
这个查询很棘手,主要原因有两个:
没有 array operator 可以将整个数组与 LIKE
模式的数组进行比较。 (没有带 pattern-matching 的“数组包含”运算符。)数组列必须未嵌套。
在取消嵌套后简单地计算匹配项是不够的,因为一个模式可以匹配多次,从而掩盖另一个匹配项的缺失。
像这样改写任务:
"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。参见:
- Any downsides of using data type "text" for storing strings?
- PostgreSQL ignores index, runs seq scan
我有以下 table:
╔════╦════════════════════════════════════════╗
║ id ║ value ║
╠════╬════════════════════════════════════════╣
║ 1 ║ ['friend', 'apple', 'cat'] ║
║ 2 ║ ['cat', 'friend', 'dog'] ║
║ 3 ║ ['pasta', 'best-friend', 'lizard'] ║
║ 4 ║ ['wildcat', 'potato', 'alices-friend'] ║
╚════╩════════════════════════════════════════╝
我的目标是 return 所有 value
包含给定数组的行。例如:
['friend', 'cat']
应该 return 行1
和2
.['%friend%', '%cat%']
应该 return 行1
、2
和4
.
目前我正在使用这个命令:
SELECT DISTINCT(id), value
FROM table
WHERE value @> (ARRAY['friend', 'cat']::VARCHAR[]);
但是当 (array['%friend%', '%cat%']::varchar[])
.
因为它适用于示例 1,我认为问题出在 %
符号上,但我不知道如何处理这个问题,因为我不需要显式匹配这些值。
您想要在数组列 value
中匹配给定数组中的 每个 LIKE
模式匹配项。
这个查询很棘手,主要原因有两个:
没有 array operator 可以将整个数组与
LIKE
模式的数组进行比较。 (没有带 pattern-matching 的“数组包含”运算符。)数组列必须未嵌套。在取消嵌套后简单地计算匹配项是不够的,因为一个模式可以匹配多次,从而掩盖另一个匹配项的缺失。
像这样改写任务:
"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。参见:
- Any downsides of using data type "text" for storing strings?
- PostgreSQL ignores index, runs seq scan