我在Postgres 9.2中使用varchar[]
列(varchar数组)来存储一些标签。在按标记检索行时,我希望查询不区分大小写。但是,我想保留在UI中显示的大小写(因此我不能将所有内容都存储为小写)。
所以,我的问题是如何通过varchar数组在Postgres中创建不区分大小写的索引?一种可能的方法是在列上创建功能性GIN索引。怎么做到这一点?还有其他方法吗?
答案 0 :(得分: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;
请注意,我也在修剪空格标签。这对您来说可能不是必需的,但我通常是为了保持一致。
测试:
SELECT array_lowercase(array['Hello','WOrLD']);
array_lowercase
-----------------
{hello,world}
(1 row)
如Saurabh所述,您可以创建一个GIN索引:
CREATE INDEX ix_tags ON tagtable USING GIN(array_lowercase(tags));
并查询:
SELECT * FROM tagtable WHERE ARRAY['mytag'::varchar] && array_lowercase(tags);
更新: WHILE
与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)
返回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)
更新2: FOREACH
与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'
结果似乎与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)
虽然我的测试并不严格,但我确实多次运行每个版本并发现数字具有代表性,这表明SQL方法(array_agg / unnest)是最快的。
答案 1 :(得分:1)
使用自定义pgplsql函数找到一种可能的方法:
首先声明一个自定义函数,该函数将varchar []数组作为输入,并返回一个新数组,其中所有元素都转换为小写。 (这是我第一次编写PL / SQL,所以这可能是非常低效的代码。)
CREATE OR REPLACE FUNCTION array_lowercase(varchar[]) RETURNS varchar[] AS $$
DECLARE
i INTEGER;
l INTEGER;
r VARCHAR[];
inp ALIAS FOR $1;
BEGIN
i := 1;
l := array_length($1, 1);
WHILE i <= l LOOP
r[i] = lower(inp[i]);
i := i + 1;
END LOOP;
RETURN r;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
接下来,使用新定义的array_lowercase
函数在表达式上创建GIN索引:
create index hotel_bookings_tags on hotel_bookings using gin(array_lowercase(tags));
现在在查询中使用它(验证它是否使用EXPLAIN使用索引):
select * from posts where array[(varchar 'some_tag')] && array_lowercase(tags);
答案 2 :(得分:0)
不确定它是否有帮助,但我正在为text[]
搜索类似内容,并使用了类型转换:
select id from product where lower(tags::text)::text[] && array['tat'];