在Postgres字符串数组上创建不区分大小写的索引

Sau*_*nda 3 postgresql database-indexes postgresql-9.2

varchar[]在Postgres 9.2中使用了一个列(varchar数组)来存储一些标签.在按标记检索行时,我希望查询不区分大小写.但是,我想保留在UI中显示的大小写(因此我不能只将小部件存储为小写).

所以,我的问题是如何在varchar数组中在Postgres中创建一个不区分大小写的索引?一种可能的方法是在列上创建功能性GIN索引.怎么做到这一点?还有其他方法吗?

mar*_*rcj 5

@Saurabh Nanda:与您发布的内容类似,您还可以创建一个简单的函数将varchar数组转换为小写,如下所示:

CREATE OR REPLACE FUNCTION array_lowercase(varchar[]) RETURNS varchar[] AS
$BODY$
  SELECT array_agg(q.tag) FROM (
    SELECT btrim(lower(unnest($1)))::varchar AS tag
  ) AS q;
$BODY$
  language sql IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)

请注意,我也在修剪空格标签.这对您来说可能不是必需的,但我通常是为了保持一致性.

测试:

SELECT array_lowercase(array['Hello','WOrLD']);
 array_lowercase 
-----------------
 {hello,world}
(1 row)
Run Code Online (Sandbox Code Playgroud)

如Saurabh所述,您可以创建GIN索引:

CREATE INDEX ix_tags ON tagtable USING GIN(array_lowercase(tags));
Run Code Online (Sandbox Code Playgroud)

并查询:

SELECT * FROM tagtable WHERE ARRAY['mytag'::varchar] && array_lowercase(tags);
Run Code Online (Sandbox Code Playgroud)

更新:WHILE vs array_agg/unnest的性能

我创建了100K 10个元素text[]数组的表(12个字符随机混合大小写字符串)并测试了每个函数.

返回了array_agg/unnest函数:

EXPLAIN ANALYZE VERBOSE SELECT array_lowercase(data) FROM test;
                                                       QUERY PLAN                                                       
------------------------------------------------------------------------------------------------------------------------
 Seq Scan on public.test  (cost=0.00..28703.00 rows=100000 width=184) (actual time=0.320..3041.292 rows=100000 loops=1)
   Output: array_lowercase((data)::character varying[])
 Total runtime: 3174.690 ms
(3 rows)
Run Code Online (Sandbox Code Playgroud)

WHILE函数返回:

EXPLAIN ANALYZE VERBOSE SELECT array_lowercase_while(data) FROM test;
                                                       QUERY PLAN                                                       
------------------------------------------------------------------------------------------------------------------------
 Seq Scan on public.test  (cost=0.00..28703.00 rows=100000 width=184) (actual time=5.128..4356.647 rows=100000 loops=1)
   Output: array_lowercase_while((data)::character varying[])
 Total runtime: 4485.226 ms
(3 rows)
Run Code Online (Sandbox Code Playgroud)

更新2: FOREACH vs. WHILE 作为最后的实验,我更改了WHILE函数以使用FOREACH:

CREATE OR REPLACE FUNCTION array_lowercase_foreach(p_input varchar[]) RETURNS varchar[] AS $BODY$
DECLARE
    el text;
    r varchar[];
BEGIN
    FOREACH el IN ARRAY p_input LOOP
        r := r || btrim(lower(el))::varchar;
    END LOOP;
    RETURN r;
END;
$BODY$
  language 'plpgsql'
Run Code Online (Sandbox Code Playgroud)

结果似乎类似于WHILE:

EXPLAIN ANALYZE VERBOSE SELECT array_lowercase_foreach(data) FROM test;
                                                       QUERY PLAN                                                       
------------------------------------------------------------------------------------------------------------------------
 Seq Scan on public.test  (cost=0.00..28703.00 rows=100000 width=184) (actual time=0.707..4106.867 rows=100000 loops=1)
   Output: array_lowercase_foreach((data)::character varying[])
 Total runtime: 4239.958 ms
(3 rows)
Run Code Online (Sandbox Code Playgroud)

虽然我的测试并不严格,但我确实运行了多个版本并发现数字具有代表性,这表明SQL方法(array_agg/unnest)是最快的.