9

私のデータベースにはzip、列を持つテーブルがありcodeます。ユーザーは郵便番号のリストをアップロードできますが、データベースに既に存在するものを特定する必要があります。現在、次の Hibernate クエリ (HQL) を使用してこれを行っています。

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

パラメータの値は:zipCodes、ユーザーがアップロードしたコードのリストです。ただし、私が使用している Hibernate のバージョンには、このようなリスト パラメータのサイズを制限するバグがあり、場合によってはこの制限を超えています。

そのため、郵便番号の (非常に長い可能性がある) リストのどれが既にデータベースに存在するかを把握する別の方法を見つける必要があります。ここに私が検討したいくつかのオプションがあります

オプション A

HQL の代わりに SQL を使用してクエリを書き直します。これにより 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

Load all the Zip codes in the database to a List. And on the user inputed list of Zip codes do a removeAll(databaseList).

Problem solved!

于 2012-12-10T10:26:55.240 に答える
0

米国には約45,000の郵便番号があり、毎年更新されているようです。これが年次の仕事である場合は、Javaで記述しないでください。郵便番号を新しいテーブルにロードするSQLスクリプトを作成し、次のコマンドを使用して挿入ステートメントを記述します。

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

運用担当者に、この2行のSQLスクリプトを年に1回実行してもらい、Javaコードでこれを購入しないでください。さらに、これをJavaから除外すれば、基本的に任意のアプローチを取ることができます。これは、オフピーク時に30分間実行されても誰も気にしないためです。

分割統治

于 2012-12-10T11:23:18.417 に答える
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 に答える
0

コードが主キーでクラスター化インデックスを持つ 100000 レコードのテーブルに対して 1000 コードを "検証" するとします。

  • オプション A は改善ではありません。Hibernate は同じ SELECT ... IN ... を作成します。自分で書くことができます。
  • オプション B は、現在のクエリと同様に、インデックスの使用に失敗する可能性があります。
  • オプション D は、郵便番号が任意の時点で変更されないことが確実な場合 (その可能性は低い)、または既存のコードを処理しようとして回復できる場合に適している可能性があります。
  • オプション C (一時テーブルを作成し、1000 個の INSERT ステートメントを発行し、1 つの SELECT で 1000 行を 100000 に対して結合する) は、それぞれ 1 つの新しいコードに対して 1000 個の単純でインデックスに適したクエリを発行するだけでは競争力がありません。

    SELECT COUNT(*) FROM Zip WHERE Zip.code = :newCode

于 2012-12-10T11:10:00.587 に答える
0

オプション D:
データベースからすべての既存の郵便番号を読み込み (ページネーション?)、アプリケーションで比較を行います。

オプション A について:
SQL クエリの長​​さの制限を覚えていますが、それは DB2 にありました。PostgreSQL に制限があるかどうかはわかりません。

于 2012-12-10T10:23:19.537 に答える