【问题标题】:PostgreSQL case insensitive SELECT on arrayPostgreSQL 不区分大小写的 SELECT 数组
【发布时间】:2013-04-25 01:06:22
【问题描述】:

我在这里、谷歌或文档中找不到答案...
我需要对数组类型进行不区分大小写的选择。

如果:

value = {"Foo","bar","bAz"}

我需要

SELECT value FROM table WHERE 'foo' = ANY(value)

匹配。

我尝试了很多 lower() 的组合,但都没有成功。

ILIKE 而不是 = 似乎可行,但我一直对 LIKE 感到紧张 - 这是最好的方法吗?

【问题讨论】:

  • 所以 ILIKE 被排除在下面@Chris Travers 所指出的 - 一个值很可能合法地包含下划线
  • 那么@Erwin 这不是一个愚蠢的问题吗? :) (我第一次被编辑——不是抱怨,只是着迷)
  • 恰恰相反:这是一个非常有趣的问题,IMO,它已经吸引了许多有趣的答案。但主要是我编辑了那一点,因为我们试图将问题和答案中的噪声比保持在 SO 上。一些噪音会进入 cmets。 :)

标签: sql postgresql pattern-matching case-insensitive


【解决方案1】:

这对我来说似乎很老套,但我认为它应该可以工作

SELECT value FROM table WHERE 'foo' = ANY(lower(value::text)::text[])

如果您的阵列可以有_%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;
$$;

那么你可以这样做:

SELECT value FROM table WHERE 'foo' = ANY(lower(value));

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

【讨论】:

  • 有用的技巧。我不认为有任何字符集/语言环境,lower 将转换数组语法关心的字符。 (顺便说一句,我有时希望 Pg 有数组 mapfilterfold/foldl/foldr 用于那些 PL/PgSQL 过度杀伤但纯 SQL 笨拙的情况。实际上,数组 sort 也是.)
  • @craig,同意。实际上排序可以很容易地用一个普通的 sql 函数来完成。
  • 实际上,也许值得将这些数组函数放在一起进行扩展。它们都可以很容易地成为 sql 或 pl/pgsql。
  • @ChrisTravers 轻松,但不一定高效。不幸的是,SQL 级别的数组访问并不是非常高效。有一天我会开始编写它们,C 中的数组访问看起来并不难,并且可以使用通用 opclass 操作来完成诸如排序之类的事情。
  • @ChrisTravers我实际上尝试了你的函数,但它为空数组返回 NULL,我发布了另一个使用转换为文本的答案。
【解决方案2】:

未提及的另一种方法是安装 PostgreSQL 8.4+ 附带的 the citext extension 并使用 citext 数组:

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

如果您想对此严格正确并避免扩展,您必须做一些pretty ugly subqueries,因为 Pg 没有很多丰富的数组操作,特别是没有功能映射操作。比如:

SELECT array_agg(lower(($1)[n])) FROM generate_subscripts($1,1) n;

... 其中 $1 是数组参数。在您的情况下,我认为您可以作弊,因为您不关心保留数组的顺序,因此您可以执行以下操作:

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

【讨论】:

  • 在 citext 上 +1。对于 OP 可能实用也可能不实用,但这是提及该扩展的绝佳机会。
  • @ErwinBrandstetter 是对的 - 遗憾的是我无法安装库 - 这有点像黑盒实例
  • 实际上,您可以将子查询包装在一个函数中(我刚刚将其添加到我的答案中),这样 lower(array_of_text) 就可以工作了。
【解决方案3】:

另一种选择是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;

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

SELECT value
FROM  (SELECT value, lower(unnest(value)) AS val FROM tbl) x
WHERE  val = 'foo'
GROUP  BY value;

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

SELECT value
FROM  (SELECT value, lower(unnest(value)) AS val FROM tbl) x
WHERE  val = 'foo';

'foo' 显然必须小写。
应该

如果您希望 快速 拥有一张大桌子,我会创建一个 功能性 GIN 索引

【讨论】:

  • 另外请注意,您可以使用子查询并编写一个 lower(text[]) 函数,这样它就可以工作了。
  • 虽然这有效并简化了代码,但它会更慢。您将在数组和集合表示之间来回切换。不过,您可以基于我提到的功能性 GIN 索引。然后你就有了快速简单的语法。
【解决方案4】:

我使用子选择排除值的解决方案...

and groupname not ilike all (
    select unnest(array[exceptionname||'%'])
    from public.group_exceptions
    where ...
      and ...
      )

【讨论】:

    【解决方案5】:

    正则表达式可以在大多数情况下完成这项工作

    SELECT array_to_string('{"a","b","c"}'::text[],'|') ~* ANY('{"A","B","C"}' );

    【讨论】:

      【解决方案6】:

      我发现创建自定义 PostgreSQL 函数最适合我

      CREATE OR REPLACE FUNCTION lower(text_array text[]) RETURNS text[] AS
          $BODY$
               SELECT (lower(text_array::text))::text[] 
          $BODY$
      LANGUAGE SQL IMMUTABLE;
      

      【讨论】:

        猜你喜欢
        • 2014-02-15
        • 2013-09-22
        • 1970-01-01
        • 2014-09-25
        • 2011-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多