记一个因为JDBC及PostgreSQL过于优秀而导致的坑
条评论假设存在这么一张表,记录全国各地大学的名称,以及所处的行政区划
CREATE TABLE 大学信息表 |
其中,行政区划是国家标准的,也就是你身份证开头的6位,能够精确到区、县的。两位一个级别,前六位,可以笼统的概括成,省及、市级、县级,当然还有直辖市、特别行政区什么的,先不必深究,暂且简化这个逻辑就好了。
现在问题来了,如果想统计各省份大学的数量,是不是该用这条SQL
select left(v_所处行政区划代码, 2), count(*) |
对需求敏感的小伙伴肯定看出来了,上面的2
在设计接口的时候应该设计成参数,万一要统计各城市的大学数量,直接传4
不就好了。
所以在Java
中,我们大约会写这么一段代码
PreparedStatement pstmt = conn.prepareStatement(""" |
就是用两处?
占位,然后传入相同的参数。可惜事与愿违,这段SQL
一定是报错的,会有提示
column "大学信息表.v_所处行政区划代码" must appear in the GROUP BY clause or be used in an aggregate function |
简单的说,数据库不认为select
中的left(v_所处行政区划代码, ?)
跟group by
中的left(v_所处行政区划代码, ?)
是一个东西,所以认为我们在查询了一个没有group by
也没有聚合函数的数据,属于SQL
语法错误。
下面尝试分析原因:
- 原始
SQL
在PostgreSQL
中独立执行,完全没问题,所以肯定不是PostgreSQL
的问题 - 用
Python
(psycopg2
)实现了同样的逻辑,然而没有报错,所以还得从JDBC
+PostgreSQL
上找原因 - 去数据库服务器看日志,发现问题,当
JDBC
请求的时候,后台日志是而当STATEMENT:
select left(v_所处行政区划代码, $1), count(*) as num
from 大学信息表
group by left(v_所处行政区划代码, $2)
DETAIL: parameters: $1 = '2', $2 = '2'Python
请求时,后台日志是是不是有一种见了鬼的感觉,select left(v_所处行政区划代码, 2), count(*) as num
from 大学信息表
group by left(v_所处行政区划代码, 2)
结论
在使用JDBC
操作PostgreSQL
时,JAVA
中的prepareStatement
会精准换算成PostgreSQL
中的PREPARE,而在PREPARE
的时候,参数还没给出,所以PostgreSQL
会认为select
的left(v_所处行政区划代码, $1)
并未参与group by
,因而PREPARE
报错,导致最终SQL
执行失败。
而在psycopg2
+PostgreSQL
的环境中,并没有像JDBC
那样充分利用PostgreSQL
的PREPARE
特性,而是在程序侧就换算好了SQL
语句,所以反而不会报错。
显然,是JDBC
的设计更细致,但是却给自己挖了个坑
本文标题:记一个因为JDBC及PostgreSQL过于优秀而导致的坑
文章作者:牧云踏歌
发布时间:2018-08-23
最后更新:2018-08-23
原始链接:http://www.kankanzhijian.com/2018/08/23/defect_jdbc_postgresql/
版权声明:本博客文章均系本人原创,转载请注名出处