3

問題の概要: Java で SQLite データベースを使用して SQL クエリを実行しようとすると、SQL ステートメントが execute() または executeQuery() メソッドから返されません。つまり、この SQL ステートメントを実行すると、システムが「ハング」します。

質問: ResultSet が「返されない」理由を説明するために、私が間違っていることは何ですか?

トラブルシューティング 問題を絞り込も うとしましたが、問題は Java の execute() または executeQuery() にあるようです。ResultSet が返されないようです。たとえば、まったく同じクエリを SQLite で (つまり、SQLite DB マネージャーを使用して) 直接実行してみました。クエリ (Java の外部) は約 5 ミリ秒で実行され、有効な結果セットが返されます。

注: 例外はスローされません。システムは単に「ハング」しているように見え、手動で強制終了するまで応答しなくなります。(10分以上待ちます。)

コード: このコードを大幅に編集して、問題を簡単に確認できるようにしました。(実稼働環境では、これはプリペアド ステートメントを使用します。ただし、ストレート ステートメントとプリペアド ステートメント バージョンの両方の方法でエラーが発生します。)

基本的に、SELECT は単一の DB アイテムを返すため、ユーザーはそのアイテムを確認できます。

Statement st = conn.createStatement() ;
ResultSet rs = st.executeQuery("SELECT DISTINCT  d1.id, d1.sourcefullfilepath, " +
    "d1.sourcefilepath, d1.sourcefilename, d1.classificationid, d1.classid, " +
    "d1.userid  FROM MatterDataset,   (SELECT MatterDataset.id, " + 
    "MatterDataset.sourcefullfilepath, MatterDataset.sourcefilepath, " +
    "MatterDataset.sourcefilename, MatterDataset.matterid   , " +
    "DocumentClassification.classificationid, DocumentClassification.classid," +
    " DocumentClassification.userid   FROM MatterDataset    " +
    "LEFT JOIN DocumentClassification ON " +
    "DocumentClassification.documentid = Matterdataset.id    " +
    "WHERE (   DocumentClassification.classid = 1 OR  " +
    "DocumentClassification.classid = 2 )   AND " +
    "DocumentClassification.userid < 0    AND " +
    "MatterDataset.matterid = \'100\'   ) AS d1    " +
    "LEFT JOIN PrivilegeLog ON " +
    "d1.id = PrivilegeLog.documentparentid AND " +
    "d1.matterid = PrivilegeLog.matterid  " +
    "WHERE PrivilegeLog.privilegelogitemid IS NULL  " +
    "AND MatterDataset.matterid = \'100\'   " +
    "ORDER BY d1.id  LIMIT 1 ;") ;

構成: Java 6、JDBC ドライバー = Xerial sqlite-jdbc-3.7.2、SQLite 3、Windows

マイナー リビジョンの更新 : これを引き続き使用するため、SQL ステートメントの先頭に MIN(d1.id) を追加すると、少なくとも ResultSet が返されます (「ハング」ではなく)。しかし、MIN は LIMIT 機能を不要にするため、これは実際には私が望んでいたものではありません。

Statement st = conn.createStatement() ;
ResultSet rs = st.executeQuery("SELECT DISTINCT  MIN(d1.id), d1.id,
    d1.sourcefullfilepath, " +
    "d1.sourcefilepath, d1.sourcefilename, d1.classificationid, d1.classid, " +
    "d1.userid  FROM MatterDataset,   (SELECT MatterDataset.id, " + 
    "MatterDataset.sourcefullfilepath, MatterDataset.sourcefilepath, " +
    "MatterDataset.sourcefilename, MatterDataset.matterid   , " +
    "DocumentClassification.classificationid, DocumentClassification.classid," +
    " DocumentClassification.userid   FROM MatterDataset    " +
    "LEFT JOIN DocumentClassification ON " +
    "DocumentClassification.documentid = Matterdataset.id    " +
    "WHERE (   DocumentClassification.classid = 1 OR  " +
    "DocumentClassification.classid = 2 )   AND " +
    "DocumentClassification.userid < 0    AND " +
    "MatterDataset.matterid = \'100\'   ) AS d1    " +
    "LEFT JOIN PrivilegeLog ON " +
    "d1.id = PrivilegeLog.documentparentid AND " +
    "d1.matterid = PrivilegeLog.matterid  " +
    "WHERE PrivilegeLog.privilegelogitemid IS NULL  " +
    "AND MatterDataset.matterid = \'100\'   " +
    "ORDER BY d1.id  LIMIT 1 ;") ;
4

2 に答える 2

1

戻ってやり直しました。SQL 構文は、Java の外部では機能しますが、JDBC ドライバーにとっては複雑すぎるように見えました。このクリーンアップされたリビジョンは機能しているようです:

SELECT DISTINCT 
  MatterDataset.id, MatterDataset.sourcefullfilepath, MatterDataset.sourcefilepath,
  MatterDataset.sourcefilename
FROM MatterDataset , DocumentClassification 
  ON DocumentClassification.documentid = MatterDataset.id  
    AND MatterDataset.matterid = DocumentClassification.matterid
LEFT JOIN PrivilegeLog ON MatterDataset.id = PrivilegeLog.documentparentid 
  AND MatterDataset.matterid = PrivilegeLog.matterid
WHERE PrivilegeLog.privilegelogitemid IS NULL  
  AND MatterDataset.matterid = '100' 
  AND (DocumentClassification.classid = 1 OR DocumentClassification.classid = 2) 
  AND DocumentClassification.userid = -99
ORDER BY MatterDataset.id LIMIT 1;

良い教訓: SQL でできるからといって、そうすべきだというわけではありません。

このステートメントが行うことは、基本的に、PrivilegeLog テーブルにない MatterDataset テーブル内のアイテムを見つけることです。LEFT JOIN および IS NULL 構文は、「不足している」項目を見つけます。つまり、MatterDataset にはあるが PrivilegeLog にはまだないアイテムを見つけて、それらのアイテムを返したいと考えています。

于 2012-07-14T20:44:31.417 に答える
1

なんて面倒な SQL ステートメントです (ごめんなさい)。私はSQLiteを知りませんが、単純化してみませんか:

SELECT DISTINCT md.id, md.sourcefullfilepath, md.sourcefilepath, md.sourcefilename, 
                dc.classificationid, dc.classid, dc.userid
FROM MatterDataset md
LEFT JOIN DocumentClassification dc
       ON dc.documentid = md.id
      AND (dc.classid = 1 OR dc.classid = 2 )
      AND dc.userid < 0
LEFT JOIN PrivilegeLog pl
       ON md.id = pl.documentparentid 
      AND md.matterid = pl.matterid
WHERE pl.privilegelogitemid IS NULL
  AND md.matterid = \'100\'
ORDER BY md.id LIMIT 1 ;

DocumentClassification に LEFT JOIN または INNER JOIN を使用するかどうかはわかりませんでした (LEFT JOIN を使用して、WHERE ステートメントで classid と userid に要件を設定することは、私の意見では矛盾しています)。DocumentClassification が存在する必要がある場合は、INNER JOIN に変更し、classid と userid への参照を WHERE 句に入れます。結果セットに DocumentClassification が存在する場合と存在しない場合がある場合は、上記で提案したようにクエリを保持します。

于 2012-07-14T19:33:34.033 に答える