9

在我的数据库中,我有一个zip带有code列的表。用户可以上传邮政编码列表,我需要找出哪些已经在数据库中。目前,我使用以下 Hibernate 查询 (HQL) 执行此操作:

select zip.code from Zip zip
where zip.code in (:zipCodes)

:zipCodes参数的值是用户上传的代码列表。但是,在我使用的 Hibernate 版本中,有一个错误会限制此类列表参数的大小,有时我们会超出此限制。

所以我需要找到另一种方法来确定数据库中已经存在哪些(可能很长)邮政编码列表。以下是我考虑过的几个选项

选项 A

使用 SQL 而不是 HQL 重写查询。虽然这将避免 Hibernate 错误,但我怀疑如果需要检查 30,000 个邮政编码,性能将会很糟糕。

选项 B

将邮政编码列表拆分为一系列子列表,并对每个子列表执行单独的查询。同样,这将避免 Hibernate 错误,但性能可能仍然很糟糕

选项 C

使用临时表,即将要检查的邮政编码插入到临时表中,然后将其连接到zip表中。看起来这个解决方案的查询部分应该执行得相当好,但是临时表的创建和最多 30,000 行的插入不会。但也许我的方法不对,这就是我在伪 Java 代码中的想法

/**
 * Indicates which of the Zip codes are already in the database
 *
 * @param zipCodes the zip codes to check
 * @return the codes that already exist in the database
 * @throws IllegalArgumentException if the list is null or empty
 */
List<Zip> validateZipCodes(List<String> zipCodes) {

  try {
    // start transaction

    // execute the following SQL
    CREATE TEMPORARY TABLE zip_tmp
    (code VARCHAR(255) NOT NULL) 
    ON COMMIT DELETE ROWS;

    // create SQL string that will insert data into zip_tmp
    StringBuilder insertSql = new StringBuilder()

    for (String code : zipCodes) {
      insertSql.append("INSERT INTO zip_tmp (code) VALUES (" + code + ");")
    }     

    // execute insertSql to insert data into zip_tmp

    // now run the following query and return the result   
    SELECT z.*
    FROM zip z
    JOIN zip_tmp zt ON z.code = zt.code

  } finally {
    // rollback transaction so that temporary table is removed to ensure
    // that concurrent invocations of this method operate do not interfere
    // with each other
  }    
}

有没有比上面的伪代码更有效的方法来实现这一点,还是有其他我没有想到的解决方案?我正在使用 Postgres 数据库。

4

5 回答 5

1

将数据库中的所有邮政编码加载到列表中。并在用户输入的邮政编码列表上执行removeAll(databaseList).

问题解决了!

于 2012-12-10T10:26:55.240 回答
0

美国大约有 45'000 个邮政编码,而且似乎每年都会更新。如果这是一项年度工作,请不要用 java 编写它。创建一个 sql 脚本,将邮政编码加载到一个新表中并编写一个插入语句

insert XXX into zip where zip.code not in (select code from ziptemp)

让你的操作人员每年运行一次这个两行 SQL 脚本,不要在 Java 代码中购买。另外,如果您将其排除在 Java 之外,您基本上可以采取任何方法,因为没有人关心它是否在非高峰时间运行了 30 分钟。

分而治之

于 2012-12-10T11:23:18.417 回答
0

假设您针对包含 100000 条记录的表“验证”1000 个代码,其中代码是主键并具有聚集索引。

  • 选项 A 不是改进,Hibernate 将构建相同的 SELECT ... IN ... 您可以自己编写。
  • 选项 B 以及您当前的查询可能无法使用索引。
  • 如果您确定邮政编码不会在任意时间更改(这不太可能),或者如果您可以从尝试处理现有代码中恢复过来,选项 D 可能会很好。
  • 选项 C(创建一个临时表,发出 1000 个 INSERT 语句并在单个 SELECT 中将 1000 行与 100000 行连接起来)与仅针对单个新代码发出 1000 个简单且索引友好的查询相比没有竞争力:

    选择 COUNT(*) 从 Zip WHERE Zip.code = :newCode

于 2012-12-10T11:10:00.587 回答
0

选项 D:
从数据库中加载所有现有的邮政编码(分页?)并在您的应用程序中进行比较。

关于您的选项 A:
我记得 SQL 查询长度的限制,但那是在 DB2 上,我不知道 PostgreSQL 是否有限制。

于 2012-12-10T10:23:19.537 回答
0

您是否尝试过使用子查询 IN ?

http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/criterion/Subqueries.html

会是这样的

DetachedCriteria dc = DetachedCriteria.forClass(Zip.class, "zz");
//add restrictions for the previous dc

Criteria c = session.createCriteria(Zip.class, "z");
c.add(Subqueries.in("z.code" dc));

对不起,如果我弄错了代码,因为我不使用 Hibernate,所以需要一段时间

于 2012-12-10T10:27:16.540 回答