Oracle, PostgreSQL 字符串排序不一致及调整

有一个使用了不同数据库的应,Oracle 和 PostgreSQL,数据库中的记录完全相同,相同的查询语句(相同的排序,至少从字面上来说是的)取到的记录排序却不同,从而产生了 Bug。 简单演示一下默认排序各自在这两种数据库中的行为, 比如说表中有两条记录,'VFORX' 和 'ibbVA'。此处不创建物理表。

Oracle(Linux)

select * from (
    select 'VFORX' as value from dual
        union
    select 'ibbVA' from dual
) order by 1 desc;

返回的结果是 阅读全文 >>

JDBC 设置 PostgreSQL 查询中 any(?) 的参数

这段时间都纠缠于 Java 如何操作 PostgreSQL 数据库上,千方百计的为求得更好的性能。为此我们用上了 Batch, 或用 id = any(?) 这种更 PostgreSQL 化的数组参数操作。其实它还有更多数组方面的花样可以玩,毕竟 PostgreSQL 数据库有一种广纳百川的胸怀,总有好的新特性能在 PostgreSQL 中首先体验到。

回到之前的一篇 postgres in (?,?) 和 =any(?) 用法/性能对比,其中关于如何向查询语句中 id = any(?) 占位符传入数组参数的代码是

在 PreparedStatement(PgPreparedStatement) 中设置数组参数的函数是用 阅读全文 >>

JDBC 批量调用数据库 SQL, 函数与存储过程

继续上一篇数据库相关操作的话题,在有大量的数据操作时(如增删改,甚至调用函数或存储过程),我们应该尽可能的采用批量化操作(先摆下结论,后面我们会看到原由)。想像一下我们要向数据库插入 10 万条记录,如果逐条插入的话,客户端与数据库之间将会有 10 万网络请求响应来回; 而假如以 1000 条记录为一个 batch, 客户端与数据库之间的网络请求响应次数将缩小到 100。 业务数据的内容总量未变,但 Batch 操作除了可重用预编译的 Statement 外还, 可避免每次请求中重复的元数据,所以从 100,000 到 100 的缩减在时效上的表现是非常可观的,有时就是 60 分钟与 1 分钟的区别(在最后面测试结果显示这一差异更为恐怖)。

当然, JDBC 的批处理功能具体还要相应驱动的支持,通过数据库连接的 conn.getMetaData().supportsBatchUpdates() 可探知是否支持批量操作。

API 方面, 在  Statement 接口中定义了如下 batch 相关的操作方法

  1. void addBatch(String sql): 将显式的 SQL 语句编入到当前 Batch 中
  2. void clearBatch(): 清除当前 Batch 列表,以便于建立新的 Batch
  3. int[] executeBatch(): 执行当前 Batch 列表中的语句,返回每条语句受影响行数组成的数组。0 可能表示执行语句无法确知受影响的行
  4. long[] executeLargeBatch(): 当 Batch 中语句受影响行数可能会超过整数最大值时用这个

阅读全文 >>

postgres in (?,?) 和 =any(?) 用法/性能对比

刚刚回顾了一下 JDBC 操作 SQL Server 时如何传入列表参数,即如何给 in (?) 条件直接传入一个列表参数,然而本质上是不支持,最终不得不展开为 in (?, ?,...?) 针对每个元素单独设置参数,不定长的参数对于重用已编译 PreparedStatement 语句的帮助不大。

那么 JDBC 操作 PostgreSQL 是何种状态呢?展开为多个参数当然是有效的。继续尝试 Spring 提供的 NamedParameterJdbcTemplate 的操作方式

String query = "select * from users where id in (:ids)";
Map<String, Object> parameters = new HashMap<>();
parameters.put("ids", IntStream.rangeClosed(1, 5).boxed().collect(toList()));
List<Map<String, Object>> maps = namedParameterJdbcTemplate.queryForList(query, parameters);

执行后查看到实际执行的语句是

select * from users where id in (?, ?, ?, ?, ?)

阅读全文 >>

Python 中创建 PostgreSQL 数据库连接池

征战 Java 多年,习惯于使用数据库之前都必须创建一个连接池,即使是单线程的应用,只要有多个方法中需用到数据库连接,建立一两个连接的也会考虑先池化他们。连接池的好处多多,1) 如果反复创建连接相当耗时,2) 对于单个连接一路用到底的应用,有连接池时避免了数据库连接对象传来传去,3) 忘记关连接了,连接池幸许还能帮忙在一定时长后关掉,当然密集取连接的应用势将耗尽连接,3) 一个应用打开连接的数量是可控的

接触到 Python 后,在使用 PostgreSQL 也自然而然的考虑创建连接池,使用时从池中取,用完后还回去,而不是每次需要连接时创建一个物理的。Python 连接 PostgreSQL 是主要有两个包,py-postgresqlpsycopg2, 而本文的实例将使用后者。

Psycopg 在 psycopg2.pool 模块中提供了两个连接池的实现在,它们都继承自 psycopg2.pool.AbstractConnectionPool, 该抽象类的基本方法是

  1. getconn(key=None): 获取连接
  2. putconn(conn, key=None, close=False): 归还连接
  3. closeall(): 关闭连接池中的所有连接

阅读全文 >>

Java 与'嵌入式' PostgreSQL 数据库的单元测试

在我们对数据库 DAO 类进行单元测试时,通常不应该依赖于一个外部数据库,所以会选用特定比较接近于真实数据库类型的内存或嵌入式数据库,如 HSQLDB(HyperSQL), H2, Derby 等。但总难免会用到特定数据库的特性,这时候就无法用前述各种数据库进行测试了。非要单元测试中覆盖到所用的数据库特性的话可以选择用 docker,如 Testcontainers, 经过模块扩展,它可以由 docker 来启动许多种类型的数据库,MySQL, Postgres, Oracle-XE, MS SQL Server, Couchbase 等等,详情见 Database containers。刚了解到的是它的模块化的无限可能,像支持 Kafka Containers 和 Localstack Module 等。

这里就不走 Testcontainers 那条路 -- 要求构建服务器上也要有 docker。早先希望能找到一种嵌入式或内存 PostgreSQL 数据库,后来发现 PostgreSQL 未能提供 In-Process 和 In-Memory 的启动方式,好在 PostgreSQL 是开源,有人可以把它改造为小型的可由测试代码启停的本地数据库。有两个具有代表性的组件,分别是 OpenTable Embedded PostgreSQL ComponentEmbedded PostgreSQL Server,它们都号称是 Embedded,所谓嵌入式,其实是进测试进程外的数据库。

下面简单体验下两个组件的用法 阅读全文 >>

PostgreSQL 批量插入, 更新和合并操作

就在 2019 年 1 月份微软收购了 PostgreSQL 数据库的初创公司 CitusData, 在云数据库方面可以增强与 AWS 的竟争。AWS 的 RDS 两大开源数据库就是 MySQL(Aurora 和 MariaDB 是它的变种) 和 PostgreSQL。

而 PostgreSQL 跳出了普通关系型数据库的类型约束,它灵活的支持 JSON, JSONB, XML, 数组等类型。比如说字段类型可以是各种形式的数组,一维或多维。

create table t1(
    address varchar(5)[3],
    counter integer[3][3],
    schedule text[][]
)

上面只是认识了一下 PostgreSQL 这一亮眼的特性,本篇重点不在如何定义操作数组类型的字段,而是对于普通的非数组字段类型如何用与数组相关的 unnest 关键字进行记录的批量插入,更新以及合并操作。

在正式使用介入 unnest 之前先熟悉一下 PostgreSQL 的 upsert(update insert) 操作。受其他数据库的影响,总以后 PostgreSQL 也应该支持 merge into 语句,而且竟然 PostgreSQL 官方也有文档介绍 MERGE 有模有样的,然而试了一下根本就不支持 merge into 操作。 阅读全文 >>

使用 PostgreSQL 的 uuid 字段类型

上一篇 使用 SQL Server 的 uniqueidentifier 字段类型 了解了 SQL Server 中如何使用 uniqueidentifier 字段类型后,现在来看下 PostgreSQL 中如何使用 uuid 字段类型。在 PostgreSQL 的字段类型是 uuid 了,所以创建一个带有 uuid 字段的表是

CREATE TABLE customers (
    id uuid PRIMARY KEY,
    name VARCHAR(36)
);

我们这里设置 id 字段类型为 uuid, 并且它是一个主键。也可以应用函数指定它的默认值,下面将会讲述到。

然后用 SQL 语句来向该表插入记录 阅读全文 >>