JDBC: Sous sélection dans clause WHERE d'un DELETE

julien23
JDBC: Sous sélection dans clause WHERE d'un DELETE

Bonjour à tous,

J'ai une requête SQL plutôt basique qui n'a pas d'effet sur ma base SQLite.

La requête SQL est exécutée comme suit:

	private static final String sqlCleanTable = "delete from LigneTelephone where Id not in (select distinct LigneTelephoneId from TelephoneCommunication)";

	public static int cleanTable() throws Exception {
		return JDBCDatabaseManager.getStatement().executeUpdate(sqlCleanTable);
	}

Je n'ai aucune erreur ni warning SQL mais la base reste inchangée alors que la requête s'exécute parfaitement (les entrées correspondantes sont bien supprimées) avec SQLite Database Browser.

Si quelqu'un a une idée, je suis vraiment preneur.

fredericmazue

Ca sens le coup classique de la base qui n'est pas en mode autocommit par défaut.

As tu essayé de basculer sur ce mode, ou sinon d'exécuter ta requête dans une transaction ?

julien23

J'y avait déjà pensé. Je suis bien en auto-commit. D'ailleurs, si je spécifie un Id donné à la place du SELECT, ça fait bien la suppression.

J'ai plutôt l'impression que le driver JDBC n'aime pas les sous-requêtes.

Quelqu'un en sait plus à ce sujet ?

Niroken

Hello,

Ce que je vais te dire ne va ptet pas t'aider beaucoup, mais intrigué par ton soucis j'ai essayé de reproduire ton test et je n'y suis pas arrivé.

Voici mon code (super sommaire):

public class Test {
  public static void main(String[] args) throws Exception {
      Class.forName("org.sqlite.JDBC");
      Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db");
      Statement stat = conn.createStatement();
      stat.executeUpdate("delete from LigneTelephone where Id not in (select distinct LigneTelephoneId from TelephoneCommunication)");
      conn.close();
  }
}

Voici la schéma des tables, super simple aussi :

CREATE TABLE LigneTelephone (Id INTEGER PRIMARY KEY);
INSERT INTO LigneTelephone VALUES(1);
INSERT INTO LigneTelephone VALUES(2);
INSERT INTO LigneTelephone VALUES(3);
INSERT INTO LigneTelephone VALUES(4);
INSERT INTO LigneTelephone VALUES(5);
INSERT INTO LigneTelephone VALUES(6);
INSERT INTO LigneTelephone VALUES(7);
INSERT INTO LigneTelephone VALUES(8);
INSERT INTO LigneTelephone VALUES(9);
INSERT INTO LigneTelephone VALUES(10);
CREATE TABLE TelephoneCommunication (LigneTelephoneId NUMERIC);
INSERT INTO TelephoneCommunication VALUES(1);
INSERT INTO TelephoneCommunication VALUES(2);
INSERT INTO TelephoneCommunication VALUES(3);
INSERT INTO TelephoneCommunication VALUES(4);

Donc on est bien d'accord, que la requete doit supprimer tous les enregistrements de 5 à 10 inclus dans la table LigneTelephone.
Pour moi ca a fonctionné et les modifications ont été répercutées sur le fichier test.db

Pour info :
J'utilise une jvm java 1.6.0_11
Je suis sous windows vista
J'ai téléchargé le driver JDBC pour SqlLite à cette url :
http://files.zentus.com/sqlitejdbc/sqlitejdbc-v054.jar

J'espère que ca peut t'aider.

Bonne chance,
Niroken

julien23

Tout d'abord, merci de ton aide.

Niroken wrote:

Donc on est bien d'accord, que la requete doit supprimer tous les enregistrements de 5 à 10 inclus dans la table LigneTelephone.

Oui c'est bien ce à quoi je m'attends.

Niroken wrote:

J'utilise une jvm java 1.6.0_11
Je suis sous windows vista
J'ai téléchargé le driver JDBC pour SqlLite à cette url :
http://files.zentus.com/sqlitejdbc/sqlitejdbc-v054.jar
Niroken

J'ai la même JVM mais par contre pas le même driver JDBC SQLite. Moi, j'utilise celui-ci : http://www.ch-werner.de/javasqlite/

En essayant de basculer sur le tiens, j'ai d'autres problèmes de concurrence de "statements". java.sql.SQLException: cannot commit transaction - SQL statements in progress
Ça a l'air d'être un problème connu : http://www.mail-archive.com/sqlitejdbc@googlegroups.com/msg00783.html.

Niroken

Hello,

Je ne te promets rien, mais tu pourrais me passer un exemple de code qui t'a conduit à cette erreur?

Niroken

julien23

Je viens de faire le test avec ton code de test et la même version du driver JDBC que toi et c'est pas mieux. Est-ce qu'il y aurait un problème avec mon schéma de base ?

Mes deux tables sont les suivantes :

CREATE TABLE `LigneTelephone` (
`id` INTEGER PRIMARY KEY ,
`Titulaire` INTEGER
);
CREATE TABLE `TelephoneCommunication` (
`CommunicationId` INTEGER,
`Sens` char (1) ,
`user` INTEGER,
`LigneTelephoneId` INTEGER
);

Un exemple de ma base épurée est là http://dl.free.fr/q8vsMVQy5

julien23

Je lance ton test comme ça java -cp ".;sqlitejdbc-v054.jar" Test avec ma base demo1.db dans le répertoire courant.

Niroken

Hello,

J'ai une bonne et une mauvaise nouvelle.

La mauvaise c'est que je suis scié comme toi, ca n'a pas marché non plus :)
La bonne c'est que j'ai essayé avec une syntaxe "in" au lieu de "not in" et ca a marché.

Je pense donc a un bug du driver JDBC, voire même qu'il n'implémente pas la syntaxe "not in". Ceci étant, ce n'est qu'une forte conviction de ma part sans preuves.

Bon sinon puisque "in" marche je me suis dit on va prendre le problème à l'envers.
On va donc récupérer tous les enregistrements qui sont dans LigneTelephone mais qui ne sont pas dans TelephoneCommunication.

Une telle requete peut se construire comme ca :

select 
  * 
from 
  LigneTelephone lt
LEFT JOIN 
  TelephoneCommunication tc  
ON 
  lt.Id=tc.LigneTelephoneId 
WHERE 
  tc.LigneTelephoneId ISNULL

Une telle requete va faire une jointure gauche des tables LigneTelephone et TelephoneCommunication, c'est a dire que tous les éléments commun par les ids aux deux tables seront mappés et ceux de LigneTelephone qui ne sont pas dans telephoneCommunication seront ajoutés a la fin...sans mapping donc leur LigneTelephoneId sera null, c'est ceux la qu on récupère.

L'ennui d'une telle requete c'est que la jointure est couteuse vu qu'il y a bcp d'enregistrements donc je te propose une requete plus optimisée (qui supprime les doublons avant de faire la jointure) :

select 
  * 
from 
  LigneTelephone lt
LEFT JOIN 
  (select distinct LigneTelephoneId from TelephoneCommunication) tc  
ON 
  lt.Id=tc.LigneTelephoneId 
WHERE 
  tc.LigneTelephoneId ISNULL

Du coup pour supprimmer tes enregistrements tu dois faire :

delete from LigneTelephone where Id in (
select 
  * 
from 
  LigneTelephone lt
LEFT JOIN 
  (select distinct LigneTelephoneId from TelephoneCommunication) tc  
ON 
  lt.Id=tc.LigneTelephoneId 
WHERE 
  tc.LigneTelephoneId ISNULL)

Si cette solution n'est pour toi pas acceptable et que d'aventure tu n'es pas contraint par le sgdb, tu peux peut etre voir du coté de hsql, c'est une solution similaire à sqllite et dois surement implémenter le "not in".

Bonne chance,
Niroken

julien23

Merci pour tes recherches, ça m'est très utile.

Je vais jeter un coup d'oeil du côté du driver JDBC et sinon adopter ta solution (ma base n'est pas énorme).

Merci encore en tout cas.