PostgreSQL对数组不敏感的SELECT

Per*_*ryW 20 sql postgresql case-insensitive pattern-matching

我在谷歌或文档中找到答案时遇到问题......
我需要针对数组类型做一个不区分大小写的选择.

因此,如果:

value = {"Foo","bar","bAz"}
Run Code Online (Sandbox Code Playgroud)

我需要

SELECT value FROM table WHERE 'foo' = ANY(value)
Run Code Online (Sandbox Code Playgroud)

匹配.

我尝试了很多lower()的组合而没有成功.

ILIKE而不是=似乎工作,但我一直很紧张LIKE- 这是最好的方式吗?

Cra*_*ger 18

一个未提及的替代方法是安装PostgreSQL 8.4+附带citext扩展并使用以下数组citext:

regress=# CREATE EXTENSION citext;
regress=# SELECT 'foo' = ANY( '{"Foo","bar","bAz"}'::citext[] );
 ?column? 
----------
 t
(1 row)
Run Code Online (Sandbox Code Playgroud)

如果你想对此严格正确并避免扩展,你必须做一些相当丑陋的子查询,因为Pg没有很多丰富的数组操作,特别是没有功能映射操作.就像是:

SELECT array_agg(lower(($1)[n])) FROM generate_subscripts($1,1) n;
Run Code Online (Sandbox Code Playgroud)

...其中$ 1是数组参数.在你的情况下,我认为你可以作弊,因为你不关心保留数组的顺序,所以你可以这样做:

SELECT 'foo' IN (SELECT lower(x) FROM unnest('{"Foo","bar","bAz"}'::text[]) x);
Run Code Online (Sandbox Code Playgroud)


Chr*_*ers 17

这对我来说似乎很苛刻,但我认为它应该有效

SELECT value FROM table WHERE 'foo' = ANY(lower(value::text)::text[])
Run Code Online (Sandbox Code Playgroud)

ilike如果您的阵列可以有_或有问题可能会有问题%

请注意,您正在做的是将文本数组转换为单个文本字符串,将其转换为小写,然后再转换回数组.这应该是安全的.如果这还不够,你可以使用string_to_array和array_to_string的各种组合,但我认为标准的文本表示应该更安全.

更新下面的子查询解决方案,一个选项是一个简单的功能:

CREATE OR REPLACE FUNCTION lower(text[]) RETURNS text[] LANGUAGE SQL IMMUTABLE AS
$$
SELECT array_agg(lower(value)) FROM unnest($1) value;
$$;
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

SELECT value FROM table WHERE 'foo' = ANY(lower(value));
Run Code Online (Sandbox Code Playgroud)

这实际上可能是最好的方法.如果需要,您还可以在函数的输出上创建GIN索引.


Erw*_*ter 5

另一种选择是unnest()

WITH tbl AS (SELECT 1 AS id, '{"Foo","bar","bAz"}'::text[] AS value)

SELECT value
FROM  (SELECT id, value, unnest(value) AS val FROM tbl) x
WHERE  lower(val) = 'foo'
GROUP  BY id, value;
Run Code Online (Sandbox Code Playgroud)

我添加了一id列以获得完全相同的结果 - 即value如果基表中有重复项则重复。根据您的情况,您可以在查询中省略id来折叠结果中的重复项,或者如果一开始就没有重复项。还演示了一种语法替代方案:

SELECT value
FROM  (SELECT value, lower(unnest(value)) AS val FROM tbl) x
WHERE  val = 'foo'
GROUP  BY value;
Run Code Online (Sandbox Code Playgroud)

如果数组元素在小写数组中是唯一的,则甚至不需要GROUP BY, 因为 everyvalue只能匹配一次。

SELECT value
FROM  (SELECT value, lower(unnest(value)) AS val FROM tbl) x
WHERE  val = 'foo';
Run Code Online (Sandbox Code Playgroud)

'foo'显然必须是小写。
应该很快

如果你想要一个大表那么快,我会创建一个功能性的 GIN 索引