PDA

View Full Version : sqlite jdbc


sottovento
01-03-2013, 22:19
Sto usando il driver SQLiteJDBC (https://lists.hcoop.net/listinfo/sqlitejdbc) ma e' estremamente lento!!

Qualcuno conosce qualcosa di meglio?

wingman87
01-03-2013, 23:04
Non ho ancora avuto modo di usarlo ma per un mio progetto pensavo di provare questo:
https://bitbucket.org/xerial/sqlite-jdbc

sottovento
02-03-2013, 07:19
Non ho ancora avuto modo di usarlo ma per un mio progetto pensavo di provare questo:
https://bitbucket.org/xerial/sqlite-jdbc

Good to know. Lo provo subito, grazie mille!

sottovento
02-03-2013, 11:10
Ho provato, ma devo dire che le prestazioni sono deludenti anche in questo caso.

Quantomeno e' meglio del primo driver che ho provato: addirittura il primo driver falliva nell'inserimento di record nonostante i dati fossero corretti (un ciclo che inseriva migliaia di record, ma ogni tanto non ce la faceva). Mi sono messo a contare quanti record venivano inseriti e quanti persi, ed erano piu' quelli persi che inseriti.

Ho provato ad inserire record con centinaia di campi, che poi e' il caso che mi interessa. Cmq le prestazioni sono davvero terribili, soprattutto confrontate a quelle che si ottengono con altri linguaggi.

Se non ci sono altri driver jdbc per sqlite, forse e' il caso di riferirsi ad un altro database (se le prestazioni contano, ovviamente)

wingman87
02-03-2013, 11:28
Mi chiedo solo se le così scarse performance sono dovute al driver o a SQLite. Se qualcuno potesse fare una prova con python o con qualcos'altro...

cdimauro
02-03-2013, 11:49
Il driver è un layer in più alla fine. Non so com'è implementato, ma non dovrebbe mangiarsi tutto questo tempo.

Provare con Python è banale, perché c'è il modulo già integrato, e l'interfaccia con SQLite è diretta (richiama le API della DLL, peraltro già inclusa nel pacchetto standard di Python).
Ecco qui (http://docs.python.org/3/library/sqlite3.html) la documentazione. Uno script di prova si realizza in una manciata di minuti.

Consiglio Python 2.7 per fare qualche esperimento. Setup, Avanti, Avanti, Fine, e da shell:

python NomeDelloScriptDiProva.py

wingman87
02-03-2013, 11:51
Ma se ho già installato l'ultima versione posso installare anche la 2.7 senza problemi?

@sottovento: puoi postare il codice di prova che hai usato?

wingman87
02-03-2013, 12:21
Ho scritto questo codice di test:
import sqlite3

conn = sqlite3.connect('example.db')
c = conn.cursor()

# Create table
c.execute('''create table two_hundred_one (id integer,
str_1 text, str_2 text, str_3 text, str_4 text, str_5 text, str_6 text, str_7 text, str_8 text, str_9 text, str_10 text, str_11 text, str_12 text, str_13 text, str_14 text, str_15 text, str_16 text, str_17 text, str_18 text, str_19 text, str_20 text, str_21 text, str_22 text, str_23 text, str_24 text, str_25 text, str_26 text, str_27 text, str_28 text, str_29 text, str_30 text, str_31 text, str_32 text, str_33 text, str_34 text, str_35 text, str_36 text, str_37 text, str_38 text, str_39 text, str_40 text, str_41 text, str_42 text, str_43 text, str_44 text, str_45 text, str_46 text, str_47 text, str_48 text, str_49 text, str_50 text, str_51 text, str_52 text, str_53 text, str_54 text, str_55 text, str_56 text, str_57 text, str_58 text, str_59 text, str_60 text, str_61 text, str_62 text, str_63 text, str_64 text, str_65 text, str_66 text, str_67 text, str_68 text, str_69 text, str_70 text, str_71 text, str_72 text, str_73 text, str_74 text, str_75 text, str_76 text, str_77 text, str_78 text, str_79 text, str_80 text, str_81 text, str_82 text, str_83 text, str_84 text, str_85 text, str_86 text, str_87 text, str_88 text, str_89 text, str_90 text, str_91 text, str_92 text, str_93 text, str_94 text, str_95 text, str_96 text, str_97 text, str_98 text, str_99 text, str_100 text,
int_1 integer, int_2 integer, int_3 integer, int_4 integer, int_5 integer, int_6 integer, int_7 integer, int_8 integer, int_9 integer, int_10 integer, int_11 integer, int_12 integer, int_13 integer, int_14 integer, int_15 integer, int_16 integer, int_17 integer, int_18 integer, int_19 integer, int_20 integer, int_21 integer, int_22 integer, int_23 integer, int_24 integer, int_25 integer, int_26 integer, int_27 integer, int_28 integer, int_29 integer, int_30 integer, int_31 integer, int_32 integer, int_33 integer, int_34 integer, int_35 integer, int_36 integer, int_37 integer, int_38 integer, int_39 integer, int_40 integer, int_41 integer, int_42 integer, int_43 integer, int_44 integer, int_45 integer, int_46 integer, int_47 integer, int_48 integer, int_49 integer, int_50 integer, int_51 integer, int_52 integer, int_53 integer, int_54 integer, int_55 integer, int_56 integer, int_57 integer, int_58 integer, int_59 integer, int_60 integer, int_61 integer, int_62 integer, int_63 integer, int_64 integer, int_65 integer, int_66 integer, int_67 integer, int_68 integer, int_69 integer, int_70 integer, int_71 integer, int_72 integer, int_73 integer, int_74 integer, int_75 integer, int_76 integer, int_77 integer, int_78 integer, int_79 integer, int_80 integer, int_81 integer, int_82 integer, int_83 integer, int_84 integer, int_85 integer, int_86 integer, int_87 integer, int_88 integer, int_89 integer, int_90 integer, int_91 integer, int_92 integer, int_93 integer, int_94 integer, int_95 integer, int_96 integer, int_97 integer, int_98 integer, int_99 integer, int_100 integer)''')

t = ('Text_1', 'Text_2', 'Text_3', 'Text_4', 'Text_5', 'Text_6', 'Text_7', 'Text_8', 'Text_9', 'Text_10', 'Text_11', 'Text_12', 'Text_13', 'Text_14', 'Text_15', 'Text_16', 'Text_17', 'Text_18', 'Text_19', 'Text_20', 'Text_21', 'Text_22', 'Text_23', 'Text_24', 'Text_25', 'Text_26', 'Text_27', 'Text_28', 'Text_29', 'Text_30', 'Text_31', 'Text_32', 'Text_33', 'Text_34', 'Text_35', 'Text_36', 'Text_37', 'Text_38', 'Text_39', 'Text_40', 'Text_41', 'Text_42', 'Text_43', 'Text_44', 'Text_45', 'Text_46', 'Text_47', 'Text_48', 'Text_49', 'Text_50', 'Text_51', 'Text_52', 'Text_53', 'Text_54', 'Text_55', 'Text_56', 'Text_57', 'Text_58', 'Text_59', 'Text_60', 'Text_61', 'Text_62', 'Text_63', 'Text_64', 'Text_65', 'Text_66', 'Text_67', 'Text_68', 'Text_69', 'Text_70', 'Text_71', 'Text_72', 'Text_73', 'Text_74', 'Text_75', 'Text_76', 'Text_77', 'Text_78', 'Text_79', 'Text_80', 'Text_81', 'Text_82', 'Text_83', 'Text_84', 'Text_85', 'Text_86', 'Text_87', 'Text_88', 'Text_89', 'Text_90', 'Text_91', 'Text_92', 'Text_93', 'Text_94', 'Text_95', 'Text_96', 'Text_97', 'Text_98', 'Text_99', 'Text_100', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)


for id in range(100000):
c.execute('insert into two_hundred_one values (' + str(id+1) + ', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', t)

# Save (commit) the changes
conn.commit()

# We can also close the cursor if we are done with it
c.close()
Ma come prendo il tempo?

cdimauro
02-03-2013, 12:49
Da Python stesso tramite il modulo timeit (http://docs.python.org/3/library/timeit.html). Guardate gli esempi.

Altrimenti ci sono programmi esterni, come ptime (se non ricordo male).
Ma se ho già installato l'ultima versione posso installare anche la 2.7 senza problemi?
Certo. Io ho Python 2.5, 2.6, 2.7, e 3.2 installati. :cool:

P.S. Non usare range, ma xrange per Python <= 2.7.

Vincenzo1968
02-03-2013, 12:53
Sto usando il driver SQLiteJDBC (https://lists.hcoop.net/listinfo/sqlitejdbc) ma e' estremamente lento!!

Qualcuno conosce qualcosa di meglio?

http://img593.imageshack.us/img593/7364/nonaffidabile.jpg

cdimauro
02-03-2013, 13:01
Mi sono permesso di dare una sistemata al codice:
import sqlite3

conn = sqlite3.connect('example.db')
c = conn.cursor()

NumStrings = 100
NumIntegers = 100

# Create table
c.execute('create table two_hundred_one (id integer, %s)' % ', '.join(['str_%s text' % i for i in xrange(1, NumStrings + 1)] + ['int_%s integer' % i for i in xrange(1, NumIntegers + 1)]))

t = tuple(['Text_%s' % i for i in xrange(1, NumStrings + 1)] + range(1, NumIntegers + 1))

for id in range(100000):
c.execute('insert into two_hundred_one values (' + str(id + 1) + ', ?' * (NumStrings + NumIntegers) + ')', t)

# Save (commit) the changes
conn.commit()

# We can also close the cursor if we are done with it
c.close()
Così è più adattabile per cambiare velocemente quante stringhe e interi utilizzare.

Non l'ho testato, ma credo che dovrebbe andare.

wingman87
02-03-2013, 13:22
Bello, mi chiedevo giusto come si potesse fare una cosa del genere. Quello che hai fatto nel ciclo for lo sapevo fare ma non l'ho fatto per ridurre al minimo le operazioni aggiuntive a execute, però ora è molto più manutenibile.

cdimauro
02-03-2013, 13:31
Allora basta precalcolare la query, e anche quel problema viene risolto:
import sqlite3

conn = sqlite3.connect('example.db')
c = conn.cursor()

NumStrings = 100
NumIntegers = 100

# Create table
c.execute('create table two_hundred_one (id integer, %s)' % ', '.join(['str_%s text' % i for i in xrange(1, NumStrings + 1)] + ['int_%s integer' % i for i in xrange(1, NumIntegers + 1)]))

t = tuple(['Text_%s' % i for i in xrange(1, NumStrings + 1)] + range(1, NumIntegers + 1))

Query = 'insert into two_hundred_one values (%s' + ', ?' * (NumStrings + NumIntegers) + ')'

for id in xrange(1, 100001):
c.execute(Query % id, t)

# Save (commit) the changes
conn.commit()

# We can also close the cursor if we are done with it
c.close()
:)

wingman87
02-03-2013, 14:41
Giusto! Grande! Appena ho tempo aggiungo la misurazione del tempo ma sul mio pc non so se sarà molto significativo senza fare un confronto con la versione java perché ho un hd ssd...
Comunque a naso ci mette circa 20 secondi.

sottovento
02-03-2013, 15:19
Scusate, avevo dei lavori da fare fuori casa (fra l'altro, oggi qui il termometro segna -28).
Il codice non e' di facile pubblicazione poiche' si tratta di lettura dei record da un file e poi di insert nel database. Preparero' un programmino di prova, ma ovviamente sara' banale.

Nei miei test, ho provato a commentare tutte le righe relative all'inserimento dei dati in sqlite per verificare quanto pesasse il resto dell'applicazione, ed il risultato e' che l'operazione e' terminata in un batter d'occhio. Nel caso rimetta le operazioni sul database, ci impiega decine di minuti per portarla a termine. Evidentemente l'overhead della mia applicazione non e' significativo.

Anche su internet ci si lamenta delle prestazioni del driver jdbc.
Per inciso: qualcuno ha misurato - anche spannometricamente - le prestazioni della versione pitonata?

Ok, ora ceno. Dopo preparo i test.

Vincenzo1968
02-03-2013, 15:44
A questo punto non sarebbe meglio utilizzare l'interfaccia nativa SQLite(anziché ODBC) tramite JNI?

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/intro.html#wp725
http://en.wikipedia.org/wiki/Java_Native_Interface
http://192.9.162.55/docs/books/jni/
http://www.ibm.com/developerworks/java/tutorials/j-jni/

sottovento
02-03-2013, 17:19
A questo punto non sarebbe meglio utilizzare l'interfaccia nativa SQLite(anziché ODBC) tramite JNI?

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/intro.html#wp725
http://en.wikipedia.org/wiki/Java_Native_Interface
http://192.9.162.55/docs/books/jni/
http://www.ibm.com/developerworks/java/tutorials/j-jni/

Certo che si puo'. Si tratta pero' di reimplementare il driver. Lo posso mettere nella gia' lunga mia to-do list, nella speranza di aver tempo per farlo.

A questo punto e' piu' facile provare un altro database, pero'.

Vincenzo1968
02-03-2013, 17:52
Certo che si puo'. Si tratta pero' di reimplementare il driver. Lo posso mettere nella gia' lunga mia to-do list, nella speranza di aver tempo per farlo.

A questo punto e' piu' facile provare un altro database, pero'.

No, che reimplementare il driver. Devi solo mappare quelle poche funzioni che ti servono. Crei una dll/so proxy che ti mappa le 2/3(4/5) funzioncine dalla dll/so SQLite.

Vincenzo1968
02-03-2013, 18:18
Nientedimeno se ne trovano di già bell'e pronti:

http://code.google.com/p/sqlite4java/

:eek:

EDIT: Eh! va che roba:


SQLiteConnection db = new SQLiteConnection(new File("/tmp/database"));
db.open(true);
...
SQLiteStatement st = db.prepare("SELECT order_id FROM orders WHERE quantity >= ?");
try {
st.bind(1, minimumQuantity);
while (st.step()) {
orders.add(st.columnLong(0));
}
} finally {
st.dispose();
}
...
db.dispose();

Vincenzo1968
02-03-2013, 18:24
Eh! nientedimeno!

http://www.codicefacile.it/tutorials/tutorials.php/28/Integrare+il+DBMS+nell%27applicazione+Java+con+SQLite


Il binding per Java

Specifico subito che esistono molti bindings per Java, tutti disponibili dal sito ufficiale. In generale esistono i bindings native e quelli 100% Java. I primi sfruttano la dll originale di SQLite, e forniscono solo un'interfaccia a tale dll tramite una libreria jar. I bindigs 100% invece hanno prestazioni leggermente minori, ma hanno il vantaggio di essere totalmente multipiattaforma, proprio perchè 100% Java.
Anche i metodi di comunicazione possono essere diversi. In questo tutorial vedremo come utilizzare un binding 100% Java a cui si può accedere con il classico JDBC, e quindi il package java.sql.

cdimauro
02-03-2013, 19:05
Giusto! Grande! Appena ho tempo aggiungo la misurazione del tempo ma sul mio pc non so se sarà molto significativo senza fare un confronto con la versione java perché ho un hd ssd...
Comunque a naso ci mette circa 20 secondi.
Da quel che ha scritto anche sottovento sembra che il suo problema sia I/O bound. Un SSD aiuta di certo in questi casi, ma la CPU rimarrà spesso a girarsi i pollici.

@sottovento: se i file da cui estrarre le informazioni sono in formato csv, Python ha un modulo già integrato (http://docs.python.org/3/library/csv.html) ad hoc. Al solito, basta dare un'occhiata veloce agli esempi per capire come usarlo (il reader è semplicissimo).

Vincenzo1968
02-03-2013, 19:11
http://img855.imageshack.us/img855/9081/sqlite4java.jpg

:eek:

VICIUS
02-03-2013, 21:47
A questo punto e' piu' facile provare un altro database, pero'.
L'ultima volta che mi è capitato di dover usare un db embedded ho provato H2 (http://www.h2database.com/) e mi ci sono trovato veramente bene.

Da quel che ha scritto anche sottovento sembra che il suo problema sia I/O bound. Un SSD aiuta di certo in questi casi, ma la CPU rimarrà spesso a girarsi i pollici.
Se il problema è veramente l'i/o e il db non è enorme si potrebbe provare ad usare un ramdisk. Su una macchina linux basta creare il file in /dev/shm.

sottovento
02-03-2013, 23:16
Nientedimeno se ne trovano di già bell'e pronti:

http://code.google.com/p/sqlite4java/

:eek:

EDIT: Eh! va che roba:


Provato, non senza fatica vista la mancanza di documentazione.
Prima impressione: wow! Va come una sgiavella! Quando prima mi servivano una ventina di minuti per fare il lavoro che mi serviva, ora servono una trentina di secondi, tempo piu' che accettabile per quel che devo fare.


Piccolo problema: ho dei record con una marea di campi, e la stringa di insert fallisce quando e' troppo lunga. Devo studiare una soluzione (probabilmente dovro' spezzare la query in una serie di insert + update).

sottovento
02-03-2013, 23:18
L'ultima volta che mi è capitato di dover usare un db embedded ho provato H2 (http://www.h2database.com/) e mi ci sono trovato veramente bene.

E' vero, H2 e' davvero un database dalle ottime caratteristiche.


Se il problema è veramente l'i/o e il db non è enorme si potrebbe provare ad usare un ramdisk. Su una macchina linux basta creare il file in /dev/shm.
Beh, nel caso in questione il problema era palesemente il driver. Cmq il suggerimento e' buono, anche se non applicabile nel mio caso. Grazie comunque!

Vincenzo1968
02-03-2013, 23:20
Provato, non senza fatica vista la mancanza di documentazione.
Prima impressione: wow! Va come una sgiavella! Quando prima mi servivano una ventina di minuti per fare il lavoro che mi serviva, ora servono una trentina di secondi, tempo piu' che accettabile per quel che devo fare.


Piccolo problema: ho dei record con una marea di campi, e la stringa di insert fallisce quando e' troppo lunga. Devo studiare una soluzione (probabilmente dovro' spezzare la query in una serie di insert + update).

:yeah: :winner: :yeah:

:bimbo:

wingman87
02-03-2013, 23:51
Alla fine il tempo l'ho misurato con time.clock() e time.perf_counter(), ci mette dai 16 ai 19 secondi.
Il db finale è grande circa 110 MB.

In python 2.7:

import sqlite3
import time

start = time.clock()

conn = sqlite3.connect('example.db')
c = conn.cursor()

NumStrings = 100
NumIntegers = 100

# Create table
c.execute('create table two_hundred_one (id integer, %s)' % ', '.join(['str_%s text' % i for i in xrange(1, NumStrings + 1)] + ['int_%s integer' % i for i in xrange(1, NumIntegers + 1)]))

t = tuple(['Text_%s' % i for i in xrange(1, NumStrings + 1)] + range(1, NumIntegers + 1))

Query = 'insert into two_hundred_one values (%s' + ', ?' * (NumStrings + NumIntegers) + ')'

for id in xrange(1, 100001):
c.execute(Query % id, t)

# Save (commit) the changes
conn.commit()

# We can also close the cursor if we are done with it
c.close()

end = time.clock()
print('Total time: ' + str(end - start))


In python 3.3 (sembrerebbe circa un secondo più lento ma visti i tempi variabili non ci metterei la mano sul fuoco):
import sqlite3
import time

NUM_TEXT = 100
NUM_INTEGER = 100

start = time.perf_counter()
conn = sqlite3.connect('example.db')
c = conn.cursor()

# Create table
c.execute('create table two_hundred_one (id integer, %s)' % ', '.join(['str_%s text' % i for i in range(1, NUM_TEXT + 1)] + ['int_%s integer' % i for i in range(1, NUM_INTEGER + 1)]))

t = tuple(['Text_%s' % i for i in range(1, NUM_TEXT + 1)] + list(range(1, NUM_INTEGER + 1)))

insert = 'insert into two_hundred_one values (%s' + ', ?' * (NUM_TEXT + NUM_INTEGER) + ')'
for id in range(1, 100001):
c.execute(insert % id, t)

# Save (commit) the changes
conn.commit()

# We can also close the cursor if we are done with it
c.close()

end = time.perf_counter()

print('Total time: ' + str(end - start))

cdimauro
03-03-2013, 06:57
Se il problema è veramente l'i/o e il db non è enorme si potrebbe provare ad usare un ramdisk. Su una macchina linux basta creare il file in /dev/shm.
E' da un pezzo che ci penso, ma ti confesso che è una gran rottura di scatole.

Ho un progetto che usa MySQL, e una batteria con circa 400 test, che impiega circa 4 minuti per essere eseguita ogni volta. Purtroppo non esiste modo di spostare programmaticamente il data store da un'altra parte. Con SQLite o Firebird posso decidere di aprire file ovunque, mentre con MySQL dovrei: fermare il server, copiare tutti i file dalla datadir alla ram disk, e farlo ripartire. Fattibile se hai una datadir piccola, ma se contiene diversi database e supera il GB, diventa troppo pesante. :muro:
Piccolo problema: ho dei record con una marea di campi, e la stringa di insert fallisce quando e' troppo lunga. Devo studiare una soluzione (probabilmente dovro' spezzare la query in una serie di insert + update).
Questo è strano. Se usi le query mettendo dei placeholder per i parametri, per poi passarli direttamente lasciando che sia il driver a inviarli al db, non dovresti avere alcun problema di questo tipo. A meno che non sia proprio un limite di SQLite.
Alla fine il tempo l'ho misurato con time.clock() e time.perf_counter(), ci mette dai 16 ai 19 secondi.
Il db finale è grande circa 110 MB.
Mi sembra un ottimo tempo. La virtual machine influisce poco per questo tipo di problemi.
In python 3.3 (sembrerebbe circa un secondo più lento ma visti i tempi variabili non ci metterei la mano sul fuoco):
E' possibile, perché in Python 3.x la gestione degli interi è un po' più lenta rispetto alla 2.x. Roba di poco conto, comunque; infatti hai appena un secondo di scarto.

sottovento
03-03-2013, 07:58
Questo è strano. Se usi le query mettendo dei placeholder per i parametri, per poi passarli direttamente lasciando che sia il driver a inviarli al db, non dovresti avere alcun problema di questo tipo. A meno che non sia proprio un limite di SQLite.

Sembra davvero un limite di sqlite. Uso i placeholder ma mi risponde che la stringa e' troppo lunga. Pero' i miei record hanno piu' di 2000 campi, e questo puo' essere un problema :D

cdimauro
03-03-2013, 09:25
Allora è molto probabile che sia quello il problema. Anche se mi sembra veramente strano: è un limite incomprensibile.

The_ouroboros
03-03-2013, 09:26
Ellapepa quanti attributi...

Inviato dal mio Sony Xperia P

cdimauro
03-03-2013, 09:28
Trovato il problema. Da qui (http://sqlite.org/limits.html):

Maximum Number Of Host Parameters In A Single SQL Statement

A host parameter is a place-holder in an SQL statement that is filled in using one of the sqlite3_bind_XXXX() interfaces. Many SQL programmers are familiar with using a question mark ("?") as a host parameter. SQLite also supports named host parameters prefaced by ":", "$", or "@" and numbered host parameters of the form "?123".

Each host parameter in an SQLite statement is assigned a number. The numbers normally begin with 1 and increase by one with each new parameter. However, when the "?123" form is used, the host parameter number is the number that follows the question mark.

SQLite allocates space to hold all host parameters between 1 and the largest host parameter number used. Hence, an SQL statement that contains a host parameter like ?1000000000 would require gigabytes of storage. This could easily overwhelm the resources of the host machine. To prevent excessive memory allocations, the maximum value of a host parameter number is SQLITE_MAX_VARIABLE_NUMBER, which defaults to 999.

The maximum host parameter number can be lowered at run-time using the sqlite3_limit(db,SQLITE_LIMIT_VARIABLE_NUMBER,size) interface.

sottovento
03-03-2013, 09:32
Bene! Beh, mi sembra che siano stati risolti tutti i problemi, no?
Le prestazioni scadenti erano dovuti al driver (o meglio, alla sua implementazione), e sappiamo anche perche' le mie query fallivano.

Grazie a tutti!!

Vincenzo1968
03-03-2013, 10:58
Aspetta aspetta!

Perché non ci spieghi un attimino come funziona sqlite4java visto che la documentazione su GoogleCode è carente?
Una sorta di mini corso, un "getting started" migliore di quello che si trova nel sito ufficiale.

;)

sottovento
03-03-2013, 17:51
Aspetta aspetta!

Perché non ci spieghi un attimino come funziona sqlite4java visto che la documentazione su GoogleCode è carente?
Una sorta di mini corso, un "getting started" migliore di quello che si trova nel sito ufficiale.

;)

Si, la documentazione ufficiale e' carentissima e lavorare con sqlite4java e' un mal di testa.
Io ho proceduto per tentativi, facendo delle supposizioni e non toccando piu' nulla quando ho ottenuto quello che mi serve.
Ovviamente il lavoro andrebbe approfondito, ma preferisco fare altro, nella mia vita :D

Collegamento: l'esempio che hai riportato funziona bene:

db = new SQLiteConnection(new File(sqliteFile));
db.open(true);

Javadoc ovviamente assente, cmq e' chiaro.

Nel tuo esempio, c'era una query ma non una insertion. Ecco qui.
Per prima cosa ho cancellato la tabella, cosi' da ricrearla (i miei campi possono cambiare a seconda del macchinario da cui ho prelevato i dati):


try
{
db.exec("BEGIN");
query = "DROP TABLE " + tableName;
SQLiteStatement stmtDrop = db.prepare(query);
stmtDrop.step();
db.exec("COMMIT");
stmtDrop.dispose();
}
catch (Exception ee)
{
//System.out.println (ee.toString());
db.exec("ROLLBACK");
}

Esiste anche la DROP TABLE IF EXISTS ma al momento della stesura del codice c'erano ancora troppe variabili in gioco e non ho provato.
Da notare:
- aprire la transazione con db.exec("BEGIN");
-creare lo statement ed eseguirlo;
- chiudere la transazione con db.exec("COMMIT");
- chiudere la transazione con db.exec("ROLLBACK") in caso di errore;
- dispose dello statement.

Esiste anche l'autocommit ed immagino esistano anche altre opzioni che probabilmente evitano questi passaggi. Non le ho provate ne' cercate.

Per creare la tabella, ho provato un altro stile, ma non so dire quale sia meglio:


db.exec("BEGIN");
String query = buildCreateTableQuery(tableName, model);
db.exec(query);
db.exec("COMMIT");

la buildCreateTableQuery() e' triviale e restituisce la stringa. Non la riporto qui per ovvii motivi.
Anche qui occorre prestare attenzione a fare il ROLLBACK nel caso l'operazione fallisca; altrimenti tutte le altre operazioni continueranno a fallire.

Per l'inserimento dei dati ho usato una preparedStatement:

String query = buildInsertQuery(tableName, model);
SQLiteStatement st = db.prepare(query);

Analogamente, buildInsertQuery() e' triviale.

In accordo con il mio software, ho poi effettuato il binding per ogni record letto da file, ed eseguito l'operazione:


for (int i = 0; i < l; i++)
{
String strType = model.getValueAt(i, 3).toString();
switch (strType)
{
case "INT":
st.bind(i+1, Integer.parseInt(vect[i]));
break;
case "DOUBLE":
st.bind(i+1, Double.parseDouble(vect[i]));
break;
default:
st.bind(i+1, vect[i]);
break;
}
}
st.step();
countSuccess++;
st.reset(true);


st.step() funziona anche nel caso delle query di update.

Non conoscendo nulla riguardo l'area di commit (grandezza, prestazioni,....) ho deciso di fare BEGIN...COMMIT/ROLLBACK ogni 100 record. Non ho voluto indagare di piu' per mancanza di tempo.

Da notare st.reset() che effettua l'unbinding in modo che al ciclo successivo possa fare il binding con i nuovi valori.
Penso che questo reset possa essere evitato in casi si usi un reference (in tal caso immagino basti aggiornare l'oggetto referenziato), ma e' solo un'ipotesi

wingman87
03-03-2013, 17:53
Ho riscritto lo stesso test anche in java e penso di aver trovato l'inghippo.
Posto le due versioni, quella lenta e quella veloce:

Lenta:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;

public class TestSqlite
{
public static int NUM_TEXT = 100;
public static int NUM_INTEGER = 100;
public static int NUM_INSERT = 10000;

public static void main(String[] args) throws ClassNotFoundException
{

long start = System.nanoTime();

// load the sqlite-JDBC driver using the current class loader
Class.forName("org.sqlite.JDBC");

Connection connection = null;
try
{
// create a database connection
connection = DriverManager.getConnection("jdbc:sqlite:sample.db");
Statement statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.

statement.executeUpdate("drop table if exists two_hundred_one");
String create = "create table two_hundred_one (id integer";
for(int i=1; i<=NUM_TEXT; i++){ create += ", text_" + i + " text"; }
for(int i=1; i<=NUM_INTEGER; i++){ create += ", int_" + i + " integer"; }
create += ")";
statement.executeUpdate(create);

String insert = "insert into two_hundred_one values(?";
for(int i=0; i<NUM_TEXT + NUM_INTEGER; i++){ insert += ", ?"; }
insert += ")";

PreparedStatement preparedStatement = connection.prepareStatement(insert);
for(int i=1; i<=NUM_TEXT; i++){ preparedStatement.setString(i + 1, "Text_" + i); }
for(int i=1; i<=NUM_INTEGER; i++){ preparedStatement.setInt(NUM_TEXT + i + 1, i); }

for(int i=1; i<=NUM_INSERT; i++){
preparedStatement.setInt(1, i);
preparedStatement.executeUpdate();
}

}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
}
finally
{
try
{
if(connection != null)
connection.close();
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}

long end = System.nanoTime();

System.out.println("Total time: " + (float)(end-start)/1000000000 );
}
}
In questa versione ho effettuato solo 10000 insert ma il tempo impiegato (l'ho fatto girare una sola volta) è stato 67 secondi :eek:

Ed ecco invece la versione veloce:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;

public class TestSqlite
{
public static int NUM_TEXT = 100;
public static int NUM_INTEGER = 100;
public static int NUM_INSERT = 100000;

public static void main(String[] args) throws ClassNotFoundException
{

long start = System.nanoTime();

// load the sqlite-JDBC driver using the current class loader
Class.forName("org.sqlite.JDBC");

Connection connection = null;
try
{
// create a database connection
connection = DriverManager.getConnection("jdbc:sqlite:sample.db");

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.

statement.executeUpdate("drop table if exists two_hundred_one");
String create = "create table two_hundred_one (id integer";
for(int i=1; i<=NUM_TEXT; i++){ create += ", text_" + i + " text"; }
for(int i=1; i<=NUM_INTEGER; i++){ create += ", int_" + i + " integer"; }
create += ")";
statement.executeUpdate(create);

String insert = "insert into two_hundred_one values(?";
for(int i=0; i<NUM_TEXT + NUM_INTEGER; i++){ insert += ", ?"; }
insert += ")";

PreparedStatement preparedStatement = connection.prepareStatement(insert);
for(int i=1; i<=NUM_TEXT; i++){ preparedStatement.setString(i + 1, "Text_" + i); }
for(int i=1; i<=NUM_INTEGER; i++){ preparedStatement.setInt(NUM_TEXT + i + 1, i); }

for(int i=1; i<=NUM_INSERT; i++){
preparedStatement.setInt(1, i);
preparedStatement.executeUpdate();
}

connection.commit();
}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
}
finally
{
try
{
if(connection != null)
connection.close();
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}

long end = System.nanoTime();

System.out.println("Total time: " + (float)(end-start)/1000000000 );
}
}
In questa ho usato gli stessi valori della versione python e il tempo impiegato è intorno ai 14 secondi, meglio di python che già mi sembrava sorprendente!

Cosa cambia nella seconda versione? Che ho usato una transazione. In sostanza la versione lenta scriveva su disco a ogni insert o quasi, infatti da esplora risorse vedevo il file del db crescere pian pianino.

Vincenzo1968
03-03-2013, 17:56
Perfetto, Sottovento, grazie mille ;)

P.S.: l'esempio che ho postato l'ho copincollato dalla pagina "getting started" del sito ufficiale. Mi sembrava un po' pochino così ti ho chiesto questa miniguida. A complemento della documentazione ufficiale. :D

sottovento
03-03-2013, 18:05
Ho riscritto lo stesso test anche in java e penso di aver trovato l'inghippo.
Posto le due versioni, quella lenta e quella veloce:

Lenta:
<cut>

In questa versione ho effettuato solo 10000 insert ma il tempo impiegato (l'ho fatto girare una sola volta) è stato 67 secondi :eek:

Ed ecco invece la versione veloce:
<cut>

In questa ho usato gli stessi valori della versione python e il tempo impiegato è intorno ai 14 secondi, meglio di python che già mi sembrava sorprendente!

Cosa cambia nella seconda versione? Che ho usato una transazione. In sostanza la versione lenta scriveva su disco a ogni insert o quasi, infatti da esplora risorse vedevo il file del db crescere pian pianino.

Quindi la differenza di prestazioni e' dovuta all'autocommit? Sicuramente e' ragionevole.
A questo punto, appena ho tempo, cerchero' di fare il confronto fra versione con autocommit = false e quella scritta con le primitive suggerite da Vincenzo, per capire quanto overhead si porta via il driver... penso sia interessante per capire quali margini ci sono, i.e. se vale la pena esser standard o esser veloci ;)

VICIUS
03-03-2013, 20:40
E' da un pezzo che ci penso, ma ti confesso che è una gran rottura di scatole.

Ho un progetto che usa MySQL, e una batteria con circa 400 test, che impiega circa 4 minuti per essere eseguita ogni volta. Purtroppo non esiste modo di spostare programmaticamente il data store da un'altra parte. Con SQLite o Firebird posso decidere di aprire file ovunque, mentre con MySQL dovrei: fermare il server, copiare tutti i file dalla datadir alla ram disk, e farlo ripartire. Fattibile se hai una datadir piccola, ma se contiene diversi database e supera il GB, diventa troppo pesante. :muro:
4 minuti per soli 400 sono veramente tanti. Lavorare così deve essere frustrante. I tempi di esecuzione dei test come sono distribuiti? Se sono solo un paio di test che prendono la maggior parte del tempo potresti pensare di separarli dal resto della suite e lanciarli tutti solo prima di un commit. Con junit è piuttosto facile fare una cosa simile usando @Category.


try
{
db.exec("BEGIN");
query = "DROP TABLE " + tableName;
SQLiteStatement stmtDrop = db.prepare(query);
stmtDrop.step();
db.exec("COMMIT");
stmtDrop.dispose();
}
catch (Exception ee)
{
//System.out.println (ee.toString());
db.exec("ROLLBACK");
}

In questo modo però se step() o la commit lanciano una eccezione la dispose() non è mai invocata. Dovresti metterla in una finally {}. Trattandosi di una libreria jni meglio non rischiare.

cdimauro
03-03-2013, 22:19
4 minuti per soli 400 sono veramente tanti. Lavorare così deve essere frustrante.
Già. E' odioso.
I tempi di esecuzione dei test come sono distribuiti? Se sono solo un paio di test che prendono la maggior parte del tempo potresti pensare di separarli dal resto della suite e lanciarli tutti solo prima di un commit.
Purtroppo sono una miriade di test abbastanza piccoli, senza nessuno che sia pesante / preponderante. Il collo di bottiglia è rappresentato proprio da MySQL, e non ci si può fare niente.

Al più, visto che ho 4 core, potrei pensare di parallelizzarli, ma sarebbe un lavoraccio pure quello (dovrei creare 4 alias per il database, e lanciare 4 processi, uno per ogni core, che si smazzano ognuno 1/4 dei test), però l'hd è soltanto uno, ed è pure meccanico (niente SSD finora), per cui non so quanto si potrebbe guadagnare alla fine.

Credo che mi convenga sbattere la testa cercando qualche soluzione per usare una ram disk. Stavo pensando di creare una datadir soltanto per questo progetto, che quindi occuperà poco spazio. Tutti gli altri database staranno nel datadir che occupa molto più spazio. Creando qualche script per impostare il datadir desiderato, dovrei riuscire a cavarmela in qualche modo.
Con junit è piuttosto facile fare una cosa simile usando @Category.
Uso Python... :stordita:

sottovento
04-03-2013, 04:05
In questo modo però se step() o la commit lanciano una eccezione la dispose() non è mai invocata. Dovresti metterla in una finally {}. Trattandosi di una libreria jni meglio non rischiare.

Certamente! Si vede che ho scritto una cosa a forza di tentativi :D

sottovento
04-03-2013, 10:59
Fatto il test jdbc driver VS sqlite4java wrapper.
In sintesi, il risultato:

Test with jdbc driver...
Total time: 21.997608
Test with wrapper...
Total time: 7.7242694


Apparentemente il vincitore e' il wrapper.
Non mi sento di affermarlo con certezza a causa della mancanza di documentazione. Sono quindi andato per tentativi e non posso escludere che manchi qualcosa che una volta aggiunto possa peggiorare i tempi. Cmq i dati ci sono, nel database.

Ho fatto il test in questo modo:
- per quanto riguarda il jdbc driver, ho preso il codice di wingman e l'ho modificato leggermente (vale a dire, non mi interessava il tempo di creazione delle tabelle, ...).
Inoltre, wingman aveva portato i cicli di set della prepared statement FUORI dal ciclo principale, visto che i dati che si scrivevano erano sempre gli stessi.
Purtroppo questo non e' possibile con il wrapper; dunque, per uniformita' di vedute (e poi, anche se i dati sono sempre gli stessi, vogliamo far finta che non lo siano) ho riportato il set della prepared nel ciclo stesso.

- per quanto riguarda il wrapper, ho fatto riferimento a quanto ho scritto precedentemente, cercando di portarlo nella maniera piu' simile possibile a quanto fatto da wingman.
E' da notare che, nonostante la banalita' del programma, ho dovuto lottare per un bel po' contro eccezioni di vario tipo poiche' qualcosa mancava sempre o era fuori posto, ecc.
Inoltre in un caso (una volta che credevo che ormai fosse tutto a posto) ho avuto un crash della jvm.

Quindi, il driver jdbc e' piu' lento ma piu' sicuro. Una cosa su cui meditare...


Qui l'applicazione che ho usato. Mi vergogno un po' vista la bassa qualita', trattasi di un'applicazione da pausa pranzo:


package sqliteperftest;

import com.almworks.sqlite4java.SQLiteConnection;
import com.almworks.sqlite4java.SQLiteStatement;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;

/**
*
* @author MEGI
*/
public class SqlitePerfTest
{

/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
try
{
System.out.println ("Test with jdbc driver...");
TestSqliteDriver.doit();
System.out.println ("Test with wrapper...");
TestSqliteWrapper.doit();
}
catch (Exception ex)
{
System.out.println ("" + ex);
}
}
}



class TestSqliteDriver
{
public static int NUM_TEXT = 100;
public static int NUM_INTEGER = 100;
public static int NUM_INSERT = 100000;

public static void doit() throws ClassNotFoundException
{

// load the sqlite-JDBC driver using the current class loader
Class.forName("org.sqlite.JDBC");

Connection connection = null;
try
{
// create a database connection
connection = DriverManager.getConnection("jdbc:sqlite:sampledriver.db");

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.

statement.executeUpdate("drop table if exists two_hundred_one");
String create = "create table two_hundred_one (id integer";
for(int i=1; i<=NUM_TEXT; i++)
{
create += ", text_" + i + " text";
}
for(int i=1; i<=NUM_INTEGER; i++)
{
create += ", int_" + i + " integer";
}
create += ")";
statement.executeUpdate(create);

String insert = "insert into two_hundred_one values(?";
for(int i=0; i<NUM_TEXT + NUM_INTEGER; i++)
{
insert += ", ?";
}
insert += ")";

long start = System.nanoTime();
PreparedStatement preparedStatement = connection.prepareStatement(insert);

for(int i=1; i<=NUM_INSERT; i++)
{
preparedStatement.setInt(1, i);
for(int j=1; j<=NUM_TEXT; j++)
{
preparedStatement.setString(j + 1, "Text_" + j);
}
for(int j=1; j<=NUM_INTEGER; j++)
{
preparedStatement.setInt(NUM_TEXT + j + 1, j);
}
preparedStatement.executeUpdate();
}

connection.commit();
long end = System.nanoTime();
System.out.println("Total time: " + (float)(end-start)/1000000000 );
}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
e.printStackTrace(System.out);
}
finally
{
try
{
if(connection != null)
{
connection.close();
}
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}

}
}

class TestSqliteWrapper
{
public static void doit()
{
String query;
SQLiteConnection db = null;
SQLiteStatement st = null;

try
{
/* Open connection */
db = new SQLiteConnection(new File("samplewrapper.db"));
db.open(true);

SQLiteStatement stmtDrop = null;
try
{
db.exec("BEGIN");
query = "drop table if exists two_hundred_one";
stmtDrop = db.prepare(query);
stmtDrop.step();
db.exec("COMMIT");
}
catch (Exception ee)
{
//System.out.println (ee.toString());
db.exec("ROLLBACK");
}
finally
{
if (stmtDrop != null)
{
stmtDrop.dispose();
}
}

db.exec("BEGIN");
String create = "create table two_hundred_one (recid integer";

for(int i=1; i<=TestSqliteDriver.NUM_TEXT; i++)
{
create += ", text_" + i + " text";
}
for(int i=1; i<=TestSqliteDriver.NUM_INTEGER; i++)
{
create += ", int_" + i + " integer";
}
create += ")";
db.exec(create);
db.exec("COMMIT");

db.exec("BEGIN");
query = "insert into two_hundred_one (recid";
for(int i=1; i<= TestSqliteDriver.NUM_TEXT; i++)
{
query += ", text_" + i;
}
for(int i=1; i<=TestSqliteDriver.NUM_INTEGER; i++)
{
query += ", int_" + i;
}

query += ") values(?";

for(int i=0; i < TestSqliteDriver.NUM_TEXT + TestSqliteDriver.NUM_INTEGER; i++)
{
query += ", ?";
}
query += ")";

long start = System.nanoTime();
st = db.prepare(query);
for(int i=1; i<=TestSqliteDriver.NUM_INSERT; i++)
{
for(int j=1; j<=TestSqliteDriver.NUM_TEXT; j++)
{
st.bind(j+1, "Text_" + j);
}

for(int j=1; j<=TestSqliteDriver.NUM_INTEGER; j++)
{
st.bind(TestSqliteDriver.NUM_TEXT + j + 1, j);
}

st.bind(1, i);
st.step();
st.reset();
}
db.exec("COMMIT");
long end = System.nanoTime();
System.out.println("Total time: " + (float)(end-start)/1000000000 );
}
catch (Exception ee)
{
System.out.println ("Exception inserting record: " + ee);
ee.printStackTrace(System.err); // test
System.exit(0); // test
}
finally
{
try
{
if (db != null)
{
db.dispose();
}
}
catch (Exception e) { }
try
{
if (st != null)
{
st.dispose();
}
}
catch (Exception e) {}
}
}
}

Vincenzo1968
04-03-2013, 12:11
:yeah: :winner: :yeah:

:bimbo:

Vincenzo1968
04-03-2013, 12:49
http://code.google.com/p/sqlite4java/

sqlite4java is not a JDBC driver. Access to the database is made through the custom interfaces of com.almworks.sqlite4java package. Tighter integration with SQLite offers better performance and features not available through JDBC interfaces.

:yeah: :winner: :yeah:

http://www.sqlite.org/cvstrac/wiki?p=SqliteWrappers
http://img402.imageshack.us/img402/2285/sqlitejavawrapper.jpg

http://img802.imageshack.us/img802/2947/sqlite3.jpg

Vincenzo1968
04-03-2013, 13:07
Si, la documentazione ufficiale e' carentissima e lavorare con sqlite4java e' un mal di testa.
Io ho proceduto per tentativi, facendo delle supposizioni e non toccando piu' nulla quando ho ottenuto quello che mi serve.
Ovviamente il lavoro andrebbe approfondito, ma preferisco fare altro, nella mia vita :D

Collegamento: l'esempio che hai riportato funziona bene:

db = new SQLiteConnection(new File(sqliteFile));
db.open(true);

Javadoc ovviamente assente, cmq e' chiaro.

Nel tuo esempio, c'era una query ma non una insertion. Ecco qui.
Per prima cosa ho cancellato la tabella, cosi' da ricrearla (i miei campi possono cambiare a seconda del macchinario da cui ho prelevato i dati):


try
{
db.exec("BEGIN");
query = "DROP TABLE " + tableName;
SQLiteStatement stmtDrop = db.prepare(query);
stmtDrop.step();
db.exec("COMMIT");
stmtDrop.dispose();
}
catch (Exception ee)
{
//System.out.println (ee.toString());
db.exec("ROLLBACK");
}

Esiste anche la DROP TABLE IF EXISTS ma al momento della stesura del codice c'erano ancora troppe variabili in gioco e non ho provato.
Da notare:
- aprire la transazione con db.exec("BEGIN");
-creare lo statement ed eseguirlo;
- chiudere la transazione con db.exec("COMMIT");
- chiudere la transazione con db.exec("ROLLBACK") in caso di errore;
- dispose dello statement.

Esiste anche l'autocommit ed immagino esistano anche altre opzioni che probabilmente evitano questi passaggi. Non le ho provate ne' cercate.

Per creare la tabella, ho provato un altro stile, ma non so dire quale sia meglio:


db.exec("BEGIN");
String query = buildCreateTableQuery(tableName, model);
db.exec(query);
db.exec("COMMIT");

la buildCreateTableQuery() e' triviale e restituisce la stringa. Non la riporto qui per ovvii motivi.
Anche qui occorre prestare attenzione a fare il ROLLBACK nel caso l'operazione fallisca; altrimenti tutte le altre operazioni continueranno a fallire.

Per l'inserimento dei dati ho usato una preparedStatement:

String query = buildInsertQuery(tableName, model);
SQLiteStatement st = db.prepare(query);

Analogamente, buildInsertQuery() e' triviale.

In accordo con il mio software, ho poi effettuato il binding per ogni record letto da file, ed eseguito l'operazione:


for (int i = 0; i < l; i++)
{
String strType = model.getValueAt(i, 3).toString();
switch (strType)
{
case "INT":
st.bind(i+1, Integer.parseInt(vect[i]));
break;
case "DOUBLE":
st.bind(i+1, Double.parseDouble(vect[i]));
break;
default:
st.bind(i+1, vect[i]);
break;
}
}
st.step();
countSuccess++;
st.reset(true);


st.step() funziona anche nel caso delle query di update.

Non conoscendo nulla riguardo l'area di commit (grandezza, prestazioni,....) ho deciso di fare BEGIN...COMMIT/ROLLBACK ogni 100 record. Non ho voluto indagare di piu' per mancanza di tempo.

Da notare st.reset() che effettua l'unbinding in modo che al ciclo successivo possa fare il binding con i nuovi valori.
Penso che questo reset possa essere evitato in casi si usi un reference (in tal caso immagino basti aggiornare l'oggetto referenziato), ma e' solo un'ipotesi

Perché non proponi di aggiungere questa tua miniguida nel sito ufficiale?

sottovento
04-03-2013, 13:30
Perché non proponi di aggiungere questa tua miniguida nel sito ufficiale?

No dai, e' solo un test, e non sono nemmeno sicuro che sia 100% corretto! Per esempio, manca la chiusura del database alla fine. Non e' un codice di qualita', e' solo un esempio per voi, cari amici :D

Vincenzo1968
04-03-2013, 14:08
Si beh, quando hai cinque minuti di tempo puoi aggiustarlo un attimino e postarlo su google code. Secondo me è molto meglio del "getting started" ufficiale.

;)

Comunque, dico, posta non il confronto con jdbc, ma questo: http://www.hwupgrade.it/forum/showpost.php?p=39121903&postcount=35

Vincenzo1968
05-03-2013, 14:17
vincenzo@mar mar 05$ javac -cp '.:sqlite-jdbc-3.7.2.jar:sqlite4java.jar:h2-1.3.170.jar' SqlitePerfTest.java
vincenzo@mar mar 05$ java -cp '.:sqlite-jdbc-3.7.2.jar:sqlite4java.jar:h2-1.3.170.jar' SqlitePerfTest
Test with jdbc driver...
Total time: 21.36797
Test with wrapper...
Total time: 7.377926
Test with h2 driver...
Total time: 28.130777

:yeah: :winner: :yeah:

H2 http://www.hwupgrade.org/public/style_emoticons/default/lol.png


//package sqliteperftest;

import com.almworks.sqlite4java.SQLiteConnection;
import com.almworks.sqlite4java.SQLiteStatement;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.*;

/**
*
* @author MEGI
*/
public class SqlitePerfTest
{

/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
try
{
System.out.println ("Test with jdbc driver...");
TestSqliteDriver.doit();
System.out.println ("Test with wrapper...");
TestSqliteWrapper.doit();
System.out.println ("Test with h2 driver...");
TestH2Driver.doit();
}
catch (Exception ex)
{
System.out.println ("" + ex);
}
}
}

class TestH2Driver
{
public static int NUM_TEXT = 100;
public static int NUM_INTEGER = 100;
public static int NUM_INSERT = 100000;

public static void doit() throws ClassNotFoundException
{

// load the sqlite-JDBC driver using the current class loader
Class.forName("org.h2.Driver");

Connection connection = null;
try
{
// create a database connection
connection = DriverManager.getConnection("jdbc:h2:sampleh2driver.db", "sa", "");

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.

statement.executeUpdate("drop table if exists two_hundred_one");
String create = "create table two_hundred_one (id integer";
for(int i=1; i<=NUM_TEXT; i++)
{
create += ", text_" + i + " text";
}
for(int i=1; i<=NUM_INTEGER; i++)
{
create += ", int_" + i + " integer";
}
create += ")";
statement.executeUpdate(create);

String insert = "insert into two_hundred_one values(?";
for(int i=0; i<NUM_TEXT + NUM_INTEGER; i++)
{
insert += ", ?";
}
insert += ")";

long start = System.nanoTime();
PreparedStatement preparedStatement = connection.prepareStatement(insert);

for(int i=1; i<=NUM_INSERT; i++)
{
preparedStatement.setInt(1, i);
for(int j=1; j<=NUM_TEXT; j++)
{
preparedStatement.setString(j + 1, "Text_" + j);
}
for(int j=1; j<=NUM_INTEGER; j++)
{
preparedStatement.setInt(NUM_TEXT + j + 1, j);
}
preparedStatement.executeUpdate();
}

connection.commit();
long end = System.nanoTime();
System.out.println("Total time: " + (float)(end-start)/1000000000 );
}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
e.printStackTrace(System.out);
}
finally
{
try
{
if(connection != null)
{
connection.close();
}
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}
}
}



class TestSqliteDriver
{
public static int NUM_TEXT = 100;
public static int NUM_INTEGER = 100;
public static int NUM_INSERT = 100000;

public static void doit() throws ClassNotFoundException
{

// load the sqlite-JDBC driver using the current class loader
Class.forName("org.sqlite.JDBC");

Connection connection = null;
try
{
// create a database connection
connection = DriverManager.getConnection("jdbc:sqlite:sampledriver.db");

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.

statement.executeUpdate("drop table if exists two_hundred_one");
String create = "create table two_hundred_one (id integer";
for(int i=1; i<=NUM_TEXT; i++)
{
create += ", text_" + i + " text";
}
for(int i=1; i<=NUM_INTEGER; i++)
{
create += ", int_" + i + " integer";
}
create += ")";
statement.executeUpdate(create);

String insert = "insert into two_hundred_one values(?";
for(int i=0; i<NUM_TEXT + NUM_INTEGER; i++)
{
insert += ", ?";
}
insert += ")";

long start = System.nanoTime();
PreparedStatement preparedStatement = connection.prepareStatement(insert);

for(int i=1; i<=NUM_INSERT; i++)
{
preparedStatement.setInt(1, i);
for(int j=1; j<=NUM_TEXT; j++)
{
preparedStatement.setString(j + 1, "Text_" + j);
}
for(int j=1; j<=NUM_INTEGER; j++)
{
preparedStatement.setInt(NUM_TEXT + j + 1, j);
}
preparedStatement.executeUpdate();
}

connection.commit();
long end = System.nanoTime();
System.out.println("Total time: " + (float)(end-start)/1000000000 );
}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
e.printStackTrace(System.out);
}
finally
{
try
{
if(connection != null)
{
connection.close();
}
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}

}
}

class TestSqliteWrapper
{
public static void doit()
{
String query;
SQLiteConnection db = null;
SQLiteStatement st = null;

try
{
/* Open connection */
db = new SQLiteConnection(new File("samplewrapper.db"));
db.open(true);

SQLiteStatement stmtDrop = null;
try
{
db.exec("BEGIN");
query = "drop table if exists two_hundred_one";
stmtDrop = db.prepare(query);
stmtDrop.step();
db.exec("COMMIT");
}
catch (Exception ee)
{
//System.out.println (ee.toString());
db.exec("ROLLBACK");
}
finally
{
if (stmtDrop != null)
{
stmtDrop.dispose();
}
}

db.exec("BEGIN");
String create = "create table two_hundred_one (recid integer";

for(int i=1; i<=TestSqliteDriver.NUM_TEXT; i++)
{
create += ", text_" + i + " text";
}
for(int i=1; i<=TestSqliteDriver.NUM_INTEGER; i++)
{
create += ", int_" + i + " integer";
}
create += ")";
db.exec(create);
db.exec("COMMIT");

db.exec("BEGIN");
query = "insert into two_hundred_one (recid";
for(int i=1; i<= TestSqliteDriver.NUM_TEXT; i++)
{
query += ", text_" + i;
}
for(int i=1; i<=TestSqliteDriver.NUM_INTEGER; i++)
{
query += ", int_" + i;
}

query += ") values(?";

for(int i=0; i < TestSqliteDriver.NUM_TEXT + TestSqliteDriver.NUM_INTEGER; i++)
{
query += ", ?";
}
query += ")";

long start = System.nanoTime();
st = db.prepare(query);
for(int i=1; i<=TestSqliteDriver.NUM_INSERT; i++)
{
for(int j=1; j<=TestSqliteDriver.NUM_TEXT; j++)
{
st.bind(j+1, "Text_" + j);
}

for(int j=1; j<=TestSqliteDriver.NUM_INTEGER; j++)
{
st.bind(TestSqliteDriver.NUM_TEXT + j + 1, j);
}

st.bind(1, i);
st.step();
st.reset();
}
db.exec("COMMIT");
long end = System.nanoTime();
System.out.println("Total time: " + (float)(end-start)/1000000000 );
}
catch (Exception ee)
{
System.out.println ("Exception inserting record: " + ee);
ee.printStackTrace(System.err); // test
System.exit(0); // test
}
finally
{
try
{
if (db != null)
{
db.dispose();
}
}
catch (Exception e) { }
try
{
if (st != null)
{
st.dispose();
}
}
catch (Exception e) {}
}
}
}


H2 :rotfl: