Newsgroup: it.comp.software.database
Titolo: Inserire record se non è già presente e eliminare i vecchi

Salve a tutti,
ho una semplice tabella con due (o tre, come Voi preferite ;-) ) campi:
id | description | check
Periodicamente (esempio ogni giorno, oppure ogni ora, o quello che 
preferite :-) ) leggo da qualche parte (es. un sito web) delle 
informazioni da memorizzare in questa tabella.
Bene, se la riga è gia presente nella tabella (cioè se un record con lo 
stesso id è già memorizzato), non voglio che sia inserito di nuovo 
(questo per risparmiare tempo, e risorse, o sbaglio?).
Viceversa il record sarà inserito.
Inoltre, avevo pensato di inserire un campo "check" in modo che:
- se il record esiste già nella tabella NON lo inserisco di nuovo (ma 
imposterò il campo check in modo da considerare il record come valido);
- se il record non è presente nella tabella, lo inserisco (e imposto il 
campo check in modo che il record sia considerato anch'esso valido),
- alla fine di tutti gli inserimenti, voglio eliminare i vecchi record, 
cioè quelli che non ho controllato con le due operazioni precedenti, 
poiché li considero "obsoleti".
Idee realizzative in merito?
Grazie.
--
rob4you

rob4you ha scritto:

Forse non ho capito bene, ma questa tabella ce l'ha la chiave primaria?!?
Se sì, qual è o quali sono i campi che la compongono?


che db utilizzi?

rob4you ha scritto:

ciao
che db utilizzi?

 Idee realizzative in merito?
Non ho capito forse il problema, ma a cosa ti servono check se poi devi
cancellare?
Imho svuota la tabella e reinseriscila globalmente, se non è problema (cioè
tipo via web sarebbe lento a riscaricare tutti i record ma mi sembra
strano).

> che db utilizzi?
hai ragione scusa! mySql
--
rob4you

 Se sì, qual è o quali sono i campi che la compongono?
Diciamo che i campi della tabella sono quei tre, e questo non ci piove. 
;-)
Se poi uno in particolare deve essere la chiave primaria, beh, mi pare 
cosa buona e giusta che sia il campo ID! :-)
--
rob4you

> che db utilizzi?
MySQL
--
rob4you

rob4you ha scritto:

   è proprio questo il problema, se tu scegliessi una chiave primaria "reale" non
avresti questo problema, poichè il database si "rifiuterebbe" di inserire un record
duplicato.
  che tipo di informazioni devi inserire?

 strano).
Io non vorrei svuotare tutta la tabella e poi reinserirla, perché IMO 
penso (anzi se mi correggete ne sarei felice), che svuotare tutta la 
tabella e poi reinserire TUTTI i record sarebbe una cosa gravosa, 
considerando che il tutto dovrei farlo tramite web (PHP a esser 
precisi).
Spiego meglio il problema, magari viene meglio la soluzione:
- da un sito web di annunci prelevo periodicamente (es. ogni due ore) 
le varie offerte, e le inserisco nel db
- due ore dopo vado a setacciare di nuovo il sito web alla ricerca 
nuovamente delle offerte da inserire nel db.
Presumibilmente le offerte "nuove" saranno poche, ecco perché non 
voglio "cancellare" tutta la tabella e reinserire tutto daccapo.
Inoltre alcune offerte che io ho memorizzato nel mio db due ore fa, 
adesso probabilmente non saranno più valide (ad esempio sono offerte 
scadute, quindi non più valide).
Vorrei quindi:
- inserire le offerte che non sono già presenti nel db
- cancellare le offerte che non sono più valide
Ecco perché avevo pensato a un campo di controllo.
Claro?? :-)
--
rob4you

   che tipo di informazioni devi inserire?
Leggi in merito il mio reply più in basso.
--
rob4you

 Claro?? :-)
no, non vedo l'utilità del campo di controllo :)

 4- inserire le nuove offerte
Così come non so quali sono quelle vecchie, come faccio a sapere quali 
sono quelle nuove?
Ti propongo la soluzione che mi ha fornito un membro del ng 
comp.databases.mysql, semplice e buona (credo) quanto gli spaghetti al 
pomodoro:
////all'inizio di tutto
UPDATE `simple_table` SET `check` = 0;
////per ogni annuncio
INSERT INTO `simple_table` (`id,`description`,`check`) VALUES
(1,'FRED',1) ON DUPLICATE KEY UPDATE `check` = 1;
////alla fine di tutto
DELETE FROM `simple_table` WHERE `check` = 0
very good, isnt it? :-)
ahhh...sti americani!!
--
rob4you

 ahhh...sti americani!!
  si ma questa è la soluzione ad un problema che non avresti dovuto avere ... ;)

   si ma questa è la soluzione ad un problema che non avresti dovuto avere ... 
Capito ora?
--
rob4you

rob4you ha scritto:

allora dove sta il problema? se il campo id è chiave primaria implica 
che non ci possono essere duplicati... quindi il problema che hai 
esposto mi sembra inesistente... se l'id c'è già l'insert fallirà 
altrimenti no.

rob4you ha scritto:

Non riesco a intuire cosa ci possa essere in tali campi, ma in base aLLA 
descriozione che hai fatto ti serve usare REPLACE
Ha la stessa sintassi di INSERT
mysql> REPLACE INTO t(id,description) VALUES(1,'DESC1');
Cosa fa REPLACE?
- se nessun valore duplica un campo indice fa l'insert del record, 
esattamente come INSERT
- se qualche valore duplica un campo indice, sovrascrive il vecchio 
record con qello nuovo
esempio pratico:
mysql> create table prova(
     -> id int,
     -> description text,
     -> primary key(id));
Query OK, 0 rows affected (0.07 sec)
mysql> replace into prova values(1,'un record');
Query OK, 1 row affected (0.02 sec)
mysql> replace into prova values(2,'un altro record');
Query OK, 1 row affected (0.00 sec)
mysql> replace into prova values(3,'un terzo record');
Query OK, 1 row affected (0.00 sec)
mysql> select * from prova;
+----+-----------------+
| id | description     |
+----+-----------------+
|  1 | un record       |
|  2 | un altro record |
|  3 | un terzo record |
+----+-----------------+
3 rows in set (0.00 sec)
mysql> replace into prova values(2,'nuovo record');
Query OK, 2 rows affected (0.00 sec)
mysql> select * from prova;
+----+-----------------+
| id | description     |
+----+-----------------+
|  1 | un record       |
|  2 | nuovo record    |
|  3 | un terzo record |
+----+-----------------+
3 rows in set (0.00 sec)
Se usi REPLACE non ti serve più nemmeno il campo check
ciao
-- 
Corrado Pandiani
MySQL 5 DBA Certified
MySQL 5 Developer Certified
http://gnu.inter.it/blogs/ilcorra
http://www.pandiani.com

 inesistente... se l'id c'è già l'insert fallirà altrimenti no.
Infatti il problema non è l'inserimento, bensì la cancellazione delle 
righe che non vanno più bene. :-)
Se ti interessano maggiori info leggi gli altri mex del 3d, capirai 
meglio il problema.
--
rob4you

> Se usi REPLACE non ti serve più nemmeno il campo check
Ok, ma come risolvo l'eliminazione delle righe che non vanno più bene? 
Cioè delle righe "vecchie".
Io le informazioni da inserire le prendo da un sito web esterno, 
diciamo ogni due ore.
Se il record dopo due ore non esiste più nel sito web, allora voglio 
eliminarlo dal mio db.
Se non avessi questa necessità ovvio che andrebbe bene anche l'idea 
tua.
--
rob4you

rob4you ha scritto:

Leggendo il post delle 20:44 ho capito meglio la tua esisgenza, anzi , 
diciamo che l'hai spiegata meglio ;-)
Soluzione banalissima.
Ad ogni esecuzione dello script di parsing crei una tabella temporanea 
identica alla tua tabella
mysql> CREATE TEMPORARY TABLE temp(id int primary key, descr text);
e ci inserisci tutti gli annunci che leggi dall'altro sito.
Dopo fai un bel TRUNCATE della tua tabella e ci copi la tabella temporanea.
mysql> TRUNCATE t;
mysql> INSERT INTO t SELECT * FROM temp;
Non mi sembra niente di trascendentale, no?


> Non mi sembra niente di trascendentale, no?
Senza dubbio non è niente di trascendentale, ma io volevo evitare 
proprio questo: visto che gli annunci totali possono essere anche 
diecimila, ma quelli "nuovi" ogni due ore saranno si e no una decina, 
non mi pare una bella idea creare una tabella temporanea da diecimila 
righe, eliminare tutte e diecimila le righe nella tabella originale, e 
reinserire tutte le diecimila righe di nuovo.
Voglio dire, il gioco non vale la candela, considerando lo specifico 
fatto che le "nuove" righe saranno si e no una decina, e altrettante 
saranno le righe da considerare "vecchie" e quindi da cancellare.
Scusate, ma la soluzione che mi ha proposto un membro di 
comp.databases.mysql non piace proprio a nessuno?
////all'inizio di tutto
UPDATE `simple_table` SET `check` = 0;
////per ogni annuncio
INSERT INTO `simple_table` (`id,`description`,`check`) VALUES
(1,'FRED',1) ON DUPLICATE KEY UPDATE `check` = 1;
////alla fine di tutto
DELETE FROM `simple_table` WHERE `check` = 0
Niente tabelle temporanee, niente truncate massicci e reinserimenti 
globali, solo un semplicissimo campo di check, azzerato all'inzio, e 
posto a "true" se l'annuncio c'è ancora.
Mi sfugge qualcosa o è esattamente ciò che cerco?
--
rob4you

rob4you ha scritto:

domanda: ma il tuo script quanti annunci si "parsa" ad ogni esecuzione ?


> domanda: ma il tuo script quanti annunci si "parsa" ad ogni esecuzione ?
Il mio script si va a parsare tutto il sito ogni volta, alla ricerca 
degli annunci presenti.
--
rob4you

 annunci presenti.
Poiché se li parsa tutti, anche se non li ho contati, saranno parecchi, 
diecimila? Forse di più, ma siamo là.
Questo ogni volta che lancio in esecuzione lo script (esempio ogni ora, 
alla ricerca nuovamente di tutti gli annunci).
--
rob4you

rob4you ha scritto:

Allora ricapitoliamo.
Diciamo che hai 10000 annunci nella tua tabella.
Passano due ore, ne vengono inseriti 20 nuovi e cancellati 10 vecchi.
Il tuo script parte e si legge quindi 10010 annuci, OK?
Applicando la soluzione che mi dicevi:
- viene fatto un full scan di una tabella di 10000 righe per fare UPDATE 
`simple_table` SET `check` = 0;
- vengono fatte 10010 query di INSERT con controllo sul valore della 
chiave ed eventualmente l'update del valore di check
- viene fatto alla fine un nuovo full scan della tabella per fare il 
DELETE FROM  `simple_table` WHERE `check` = 0
Beh, tutte queste operazioni sono molto più lente da eseguire che non 
fare quello che suggerivo .... prova a implementarle entrambe, 
cronometra, e poi mi dici!


 quello che suggerivo .... 
Era proprio una risposta del genere che cercavo. Ho chiesto varie volte 
i pro e i contro delle varie soluzioni.
Proverò entrambe le soluzioni, e valuterè il tempo impiegato da 
entrambe, e poi ti faccio sapere.
Ma a questo punto, nella soluzione da te suggerita, perché passare 
dalla temporary table? A questo punto non è sufficiente troncare da 
subito la tabella con le offerte, e reinserirla da zero? Ci sono 
problemi?
--
rob4you

rob4you ha scritto:

   effettivamente ora le cose sono più chiare, credo ti convenga cancellare la tabella e
reinserire tutto da capo, valuta anche la soluzione della tabella temporanea se devi
avere una "continuità di servizio"

rob4you ha scritto:

In effetti puoi anche non usare la temporary table e fare il truncate e
gli insert successivi, ma a scapito di una problematica:
- per tutto il tempo necessario agli INSERT hai la tabella non
disponibile, mentre l'operazione INSERT INTO tabella SELECT * FROM temp;
è più veloce imponendo il Locking una volta sola. Gli INSERT separati
invece LOCKANO di volta in volta la tabella, perdendo un po' di tempo in
più. Se per te questo "momento morto" non è un problema puoi anche
evitare la tabella temporanea
Se vuoi migliorare la situazione ti conviene creare la tabella
temporanea senza chiave primaria e nessun altro indice. Gli INSERT
saranno pertanto più veloci.
Se non vuoi usare la tabella temporanea puoi migliorare la situazione
con qualche accortezza.
1) disabilitare gli indici prima di fare tutti gli insert e poi
riabilitarli alla fine
ALTER TABLE t DISABLES KEYS;
INSERTs ....
ALTER TABLE t ENABLE KEYS;
2) impostare il LOCK manualmente prima degli insert
LOCK TABLES t WRITES;
INSERTs
UNLOCK TABLES
3) se vuoi modificare un po' il tuo script puoi fare inserimenti a
blocchi del tipo
INSERT INTO t VALUES(1,'dfdfs'),(2,'addsfdsf'),(3,'sdfsdfsd'),.....;
Una domandina finale: non che mi freghi tanto, ma è lecito quello che
vuoi fare di "succhiare" tutti gli annunci di un sito? Non è che se se
ne accorgono possano prenderla male?
ciao

 ne accorgono possano prenderla male?
:-) Mi domandavo infatti come mai nessuno me lo avesse chiesto!
Comunque, dont worry, lo sto facendo solo per un mio lavoro personale.
Mi serviva una tabella popolata con campi "realistici", per fare poi 
altre operazioni più complesse. :-)
--
rob4you

 avere una "continuità di servizio"
Mi sa che infatti opterò per questa soluzione. Mi metto a fare anche un 
po' di prove in tutti i casi, così vedo chi la spunta. ;-)
--
rob4you

>
i tre suggerimenti possono essere usati tutti insieme e sono relativi
solo al caso in cui tu non voglia appoggiarti alla temporary table e,
ovviamente, la prima operazione è sicuramente il truncate
in merito agli inserimenti multipli (un solo insert per tanti record)
c'è un limite dovuto alla variabile max_packet_size (default è 1 MB)
fai gruppi di insert a blocchi di un centinaio di record e non dovresti
aver problemi

 aver problemi
Ok, grazie anche per questo suggerimento.
--
rob4you

 
l'inserimento di un record è più veloce se non si devono aggiornare
anche gli indici

 anche gli indici
Ok grazie.
Non resta che mettermi a fare le prove.
Intanto ho esposto la tua soluzione (quella con la temporary table) 
sull'altro ng, e ovviamente ;-) mi dicono che è più efficiente la loro 
soluzione.
Non resta che provare. :-)
--
rob4you