0

PostgreSQL查询中的对象名称、数据内容大小写敏感问题

wuxqing 7年前发布 | 47K 次阅读

PostgreSQL查询中的对象名称、数据内容大小写敏感问题

MySQL数据库中,查询数据是不区分大小写的,下面2条语句,获得的数据一样

SELECT username, email FROM auth_user WHERE username = 'admin' LIMIT 10;
SELECT username, email FROM auth_user WHERE username = 'Admin' LIMIT 10;

但是,PostgreSQL区分大小写,这样有时候就不方便了。那么,PostgreSQL查询如何做到忽略大小写?下面总结一些常见的方法:

一、使用 LOWER/UPPER 函数

使用LOWER/UPPER函数,把数据和条件值都转为小写或者大写,但是这样做索引会失效,如果是大数据量的情况下,性能会大幅下降

1、先做普通的查询,做为基准参考

SELECT username, email FROM auth_user WHERE username = 'Admin' LIMIT 10;
 username | email 
----------+-------------------
 Admin | 123@123.com
(1 row)
​
Time: 0.491 ms

2、再看看使用LOWER函数的情况

SELECT username, email FROM auth_user WHERE LOWER(username) = LOWER('Admin') LIMIT 10;
 username | email 
----------+-------------------
 Admin | 123@123.com
 admin | admin@123.org
(2 rows)
​
Time: 820.651 ms

结论:太慢了,慢了1600倍!!!

3、分析下索引的使用情况

EXPLAIN ANALYZE SELECT username, email FROM auth_user WHERE username = 'Admin' LIMIT 10;
 QUERY PLAN 
----------------------------------------------------------------------------------------------------------------------------------------
 Limit (cost=0.43..8.45 rows=1 width=28) (actual time=0.091..0.092 rows=1 loops=1)
 -> Index Scan using ix_auth_user_username on auth_user (cost=0.43..8.45 rows=1 width=28) (actual time=0.089..0.090 rows=1 loops=1)
 Index Cond: ((username)::text = 'Admin'::text)
 Planning time: 0.135 ms
 Execution time: 0.125 ms
(5 rows)
EXPLAIN ANALYZE SELECT username, email FROM auth_user WHERE LOWER(username) = LOWER('Admin') LIMIT 10;
 QUERY PLAN 
-------------------------------------------------------------------------------------------------------------------
 Limit (cost=0.00..101.00 rows=10 width=28) (actual time=7.422..835.354 rows=2 loops=1)
 -> Seq Scan on auth_user (cost=0.00..84397.18 rows=8356 width=28) (actual time=7.420..835.349 rows=2 loops=1)
 Filter: (lower((username)::text) = 'admin'::text)
 Rows Removed by Filter: 1583840
 Planning time: 0.142 ms
 Execution time: 835.381 ms
(6 rows)

结论:可以看到,使用LOWER/UPPER函数后,索引失效了

二、使用ILIKE

1、看看效果

SELECT username, email FROM auth_user WHERE username ILIKE 'Admin' LIMIT 10;
 username | email 
----------+-------------------
 Admin | 123@123.com
 admin | admin@123.org
(2 rows)
​
Time: 1227.317 ms

结论:看起来比使用 LOWER/UPPER 还慢!!!

SELECT username, email FROM auth_user WHERE username ILIKE '%Admin%' LIMIT 10;
 username | email 
----------------+---------------------
 Admin | 17@q1.com
 admin19880211 | 114@q2.com
 adminis啊 | 1448@q1.com
 superadmins | 132@1631.com
 admini123 | 196@qq.com
 usadmin mj | 0037@163.com
 admin | 1236@q1.com
 Adminbao | 19218@12.com
 wfadmin | wf12@sa.com
 adminpang | dms@133.com
(10 rows)
​
Time: 53.805 ms

结论:模糊查询,速度还行

2、看下索引使用情况

EXPLAIN ANALYZE SELECT username, email FROM auth_user WHERE username LIKE 'Admin' limit 10;
 QUERY PLAN 
----------------------------------------------------------------------------------------------------------------------------------------
 Limit (cost=0.43..8.45 rows=1 width=28) (actual time=0.033..0.034 rows=1 loops=1)
 -> Index Scan using ix_auth_user_username on auth_user (cost=0.43..8.45 rows=1 width=28) (actual time=0.032..0.033 rows=1 loops=1)
 Index Cond: ((username)::text = 'Admin'::text)
 Filter: ((username)::text ~~ 'Admin'::text)
 Planning time: 0.075 ms
 Execution time: 0.051 ms
(6 rows)
​
Time: 0.510 ms

结论:LIKE是使用索引的

EXPLAIN ANALYZE SELECT username, email FROM auth_user WHERE username ILIKE 'Admin' LIMIT 10;
 QUERY PLAN 
-------------------------------------------------------------------------------------------------------------------
 Limit (cost=0.00..4803.53 rows=10 width=28) (actual time=7.172..1340.952 rows=2 loops=1)
 -> Seq Scan on auth_user (cost=0.00..80218.99 rows=167 width=28) (actual time=7.172..1340.949 rows=2 loops=1)
 Filter: ((username)::text ~~* 'Admin'::text)
 Rows Removed by Filter: 1583850
 Planning time: 0.143 ms
 Execution time: 1340.984 ms
(6 rows)

结论:同样的 ILIKE 查询索引失效了

三、使用正则表达式

SELECT username, email FROM auth_user WHERE username ~* '^admin$' LIMIT 10;
 username | email 
----------+-------------------
 Admin | 1933088987@qq.com
 admin | admin@infopub.org
(2 rows)
​
Time: 761.884 ms
SELECT username, email FROM auth_user WHERE username ~* 'admin' LIMIT 10;
 username | email 
----------------+---------------------
 Admin | 17@q1.com
 admin19880211 | 114@q2.com
 adminis啊 | 1448@q1.com
 superadmins | 132@1631.com
 admini123 | 196@qq.com
 usadmin mj | 0037@163.com
 admin | 1236@q1.com
 Adminbao | 19218@12.com
 wfadmin | wf12@sa.com
 adminpang | dms@133.com
(10 rows)
​
Time: 39.301 ms

结论:速度上比ILIKE略好

随便说下几种匹配模式

模式 示例
~ 匹配正则表达式,大小写相关 username ~ 'Admin'
~* 匹配正则表达式,大小写无关 username ~* 'Admin'
!~ 不匹配 正则表达式,大小写相关 username !~ 'Admin'
!~* 不匹配 正则表达式,大小写无关 username !~* 'Admin'

上面的例子是全模糊查询,如果要全匹配,可以这样username ~* '^admin$'

四、创建索引

CREATE INDEX idx_auth_user_username ON auth_user lower(username);
# 如果希望LIKE也能使用
CREATE INDEX idx_auth_user_username ON auth_user lower(username varchar_pattern_ops);

这种方式,是创建了全部小写的索引,理论上是非常快的(最快的) 但是,某些情况下,你又想要大小写敏感了?那就不能用了如果需求就是大小写无关的,那么这是最好的方式

五、使用citext module模块

使用示例如下:

CREATE TABLE users (
 nick CITEXT PRIMARY KEY,
 pass TEXT NOT NULL
);
​
SELECT * FROM users WHERE nick = 'Larry';

citext通过转换每个字符串到小写执行比较,类似lower函数,速度也不会太快,但是使用上,SQL语句简单很多,详细参考这里:https://www.postgresql.org/docs/current/citext.html

六、表名和字段名的大小写敏感问题

PostgreSQL会自动把表名和字段名转成小写保存的,查询时也是会自动转成小写。一些程序或ORM会强制存为大小组合的名称,这个时候查询可能会报错,可以使用双引号让PostgreSQL不要做小写转换,示例如下:

SELECT username, email FROM "AuthUser" LIMIT 10;

未经许可,禁止转载!

本文由用户 wuxqing 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!

推荐阅读

PostgreSQL查询中的对象名称、数据内容大小写敏感问题

MySQL数据库中,查询数据是不区分大小写的,下面2条语句,获得的数据一样 SELECT username, email FROM auth_user WHERE username = '...

Zend Framework框架的Action大小写和请求URL大小写问题

有用过Zend Framework框架开发过项目的网友都知道 Zend Framework (ZF) MVC中的Controller和Action名称默认是不支持大小写的,这对于已经习惯了驼峰式...

SpringMVC中@ResponseBody的问题

在SpringMVC中可以在Controller的某个方法上加@ResponseBody注解,表示该方法的返回结果直接写入HTTP response body中。 但是实际使用中发现最后生成的r...

数据库问题

大家好, 我的项目采用 Struts+Spring+Hibernate开发,现在出现一个要求: 在登录验证之前,进行数据库连接状态的判断,如果连接成功,则进行登录验证;如果数据库连接是失败的,则...

AltStyle によって変換されたページ (->オリジナル) /