Lentezza nel salvataggio dati

venerdì 08 settembre 2006 - 10.34

Lindbergh Profilo | Newbie

Ciao a tutti, sono nuovo in questo forum.
Volevo partire con il mio primo post con un quesito che mi sta facendo perdere la testa...
Premetto con sui database non sono ferratissimo e forse per questo ho un problema quando devo salvare molti dati in una tabella, la mia applicazione può usare sia un database Access sia un database SQL e questo lo decide l'installatore modificando semplicemente la stringa di connessione dalla quale via codice decido se istanziare i componenti per la connessione Access o SQL.
Fatta questa premessa vi espongo il problema e spero di trovare un aiuto:

Il problema è che prima di memorizzare i dati nella tabella devo controllare se il dato è già presente quindi modificare la riga esistente oppure, se il dato non esiste, devo aggiungere una nuova riga.
I parametri di ricerca per stabilire se la riga esiste sono una stringa descrittiva e una data.

Purtroppo il metodo che ho trovato funziona bene se non fosse che per i tempi nel senso che per controllare e memorizzare circa 6000 righe il mio PC (che non ha problemi di potenza...) impiega circa 20 minuti...
La tabella in questione conteneva, quando ho fatto la prova, circa 20000 righe.

Eccovi uno stralcio di codice:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

Qualcuno sa darmi una soluzione migliore per l'inserimento dati o dirmi dove sbaglio?

Ciao e grazie
Stefano

lbenaglia Profilo | Guru

>Il problema è che prima di memorizzare i dati nella tabella devo
>controllare se il dato è già presente quindi modificare la riga
>esistente oppure, se il dato non esiste, devo aggiungere una
>nuova riga.
<SNIP>
>Qualcuno sa darmi una soluzione migliore per l'inserimento dati
>o dirmi dove sbaglio?

Ciao Stefano,

sicuramente nella tabella avrai previsto una o più colonne che costituiscono la Primary key, giusto?
In questo cado farei una cosa semplicissima:

Provo ad eseguire una INSERT. Se l'operazione va a buon fine significa che non ho avuto alcuna violazione della PK, diversamente intercetto l'errore e, se si tratta di una violazione della PK, eseguo l'UPDATE diversamente significa che c'è un problema di altra natura ed agisco di conseguenza.

In SQL Server questa logica potrebbe essere integrata in una stored procedure, ma dato che sei costretto a mantenere la compatibilità con Access, non vedo altre alternative che esegurla lato client.

>Ciao e grazie
Prego.

Ciao!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

Lindbergh Profilo | Newbie

Ciao Lorenzo, intanto grazie 1000 per la celere risposta alla quale aggiungo un paio di cose per chiarezza:
La mia tabella purtroppo x ora ha una sola chiave primaria che è l'ID auto-incrementale, per la modifica che hai suggerito tu provo a modificarla aggiungendo le altre due colonne, una per la descrizione del campo e una per la data, così facendo sicuramente la tua soluzione funziona e ti farò sapere il risultato cioè di quanto migliorano i tempi per il salvataggio dati.
La seconda cosa che volevo chiederti è dove posso trovare un esempio di come creare una stored procedure che faccia al mio caso perché sto già migrando il progetto in VS2005 e li credo di utilizzare solo SQL.

Grazie ancora e a presto
Stefano

P.S. complimenti all'amministratore / amministratori per il sito, lo sto "spulciando" ben bene ed è molto bello!

lbenaglia Profilo | Guru

>La mia tabella purtroppo x ora ha una sola chiave primaria che
>è l'ID auto-incrementale,
OK

>per la modifica che hai suggerito tu
>provo a modificarla aggiungendo le altre due colonne, una per
>la descrizione del campo e una per la data,
Gulp? E a che servirebbero queste altre 2 colonne?
Scusa, a parte l'ID non hai una o più colonne che identificano univocamente una riga? In questo caso è sufficiente definire un constraint UNIQUE e sei a posto.

"UNIQUE Constraints"
http://msdn2.microsoft.com/en-us/library/ms191166.aspx

>La seconda cosa che volevo chiederti è dove posso trovare un
>esempio di come creare una stored procedure che faccia al mio
>caso perché sto già migrando il progetto in VS2005 e li credo
>di utilizzare solo SQL.
A questo punto se deciderai di utilizzare esclusivamente SQL Server potresti servirti della funione EXISTS():

USE tempdb; GO /* Creo la tabella dbo.Students */ CREATE TABLE dbo.Students( StudentID int NOT NULL IDENTITY, FirstName varchar(10) NOT NULL, LastName varchar(10) NOT NULL, Country varchar(10) NOT NULL, CONSTRAINT PK_Students PRIMARY KEY(StudentID), CONSTRAINT UN_name UNIQUE(FirstName, LastName) ); GO /* Creo la stored procedure dbo.up_ManageStudents */ CREATE PROCEDURE dbo.up_ManageStudents( @FirstName varchar(10), @LastName varchar(10), @Country varchar(10) ) AS IF EXISTS( SELECT * FROM dbo.Students WHERE FirstName = @FirstName AND LastName = @LastName ) /* Eseguo l'UPDATE */ UPDATE dbo.Students SET FirstName = @FirstName, LastName = @LastName, Country = @Country WHERE FirstName = @FirstName AND LastName = @LastName ELSE /* Eseguo l'INSERT */ INSERT dbo.Students VALUES(@FirstName, @LastName, @Country); GO /* Aggiungo 3 studenti */ EXEC dbo.up_ManageStudents 'Lorenzo', 'Benaglia', 'Italy'; EXEC dbo.up_ManageStudents 'Luca', 'Bianchi', 'Italy'; EXEC dbo.up_ManageStudents 'Gianluca', 'Hotz', 'Swiss'; GO /* Vediamo... */ SELECT * FROM dbo.Students; GO /* Output: StudentID FirstName LastName Country ----------- ---------- ---------- ---------- 1 Lorenzo Benaglia Italy 2 Luca Bianchi Italy 3 Gianluca Hotz Swiss (3 row(s) affected) */ /* Aggiorno il mio account */ EXEC dbo.up_ManageStudents 'Lorenzo', 'Benaglia', 'USA'; GO /* Vediamo... */ SELECT * FROM dbo.Students; GO /* Output: StudentID FirstName LastName Country ----------- ---------- ---------- ---------- 1 Lorenzo Benaglia USA 2 Luca Bianchi Italy 3 Gianluca Hotz Swiss (3 row(s) affected) */ /* Pulizia */ DROP PROCEDURE dbo.up_ManageStudents; DROP TABLE dbo.Students;


>Grazie ancora e a presto
Prego.

>P.S. complimenti all'amministratore / amministratori per il sito,
>lo sto "spulciando" ben bene ed è molto bello!
Merito di David :-)

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

Lindbergh Profilo | Newbie

Rieccomi...

Mi sono spiegato male, non voglio creare altre due colonne ma cambiare la chiave primaria da ID ad altre due colonne esistenti, mi spiego meglio facendoti vedere il database che è semplicissimo:
id Contatore (chiave primaria auto-incrementale)
Descrizione Testo
DDHH Data/ora
Valore Numerico

Adesso la tabella l'ho modificata così:
id Contatore (auto-incrementale)
Descrizione Testo (chiave primaria)
DDHH Data/ora (chiave primaria)
Valore Numerico

In queso modo evito che ci siano due righe con la stessa descrizione e la stessa data...

Ho provato a fare il tutto ma purtroppo non va ancora bene e spiego il perché.
Per come ho pensato e fatto il salvataggio dati, lo posso riassumere in 4 punti:

1) Carico l'intera tabella dal database in un DataSet;
2) for di tutte le nuove righe recuperate
3) nel for devo modificare la riga del DataSet se la nuova riga ha la stessa descrizione e la stessa data altrimenti devo inserire una nuova riga con: myDataSet.Tables["Tabella"].NewRow()
4) ALLA FINE del for memorizza il DataSet nel database con myDataAdapter.Update(myDataSet)

Quindi il problema è che non mi accorgo se ci sono righe duplicate finché non faccio l'update quindi al termine dell'inserimento dati...

Come posso fare?

Grazie ancora e grazie per il codice SQL che mi sarà sicuramente utile!
Stefano


P.S. Per il sito complimenti a David allora! :-)

lbenaglia Profilo | Guru

>Per come ho pensato e fatto il salvataggio dati, lo posso riassumere
>in 4 punti:
>
>1) Carico l'intera tabella dal database in un DataSet;
>2) for di tutte le nuove righe recuperate
>3) nel for devo modificare la riga del DataSet se la nuova riga
>ha la stessa descrizione e la stessa data altrimenti devo inserire
>una nuova riga con: myDataSet.Tables["Tabella"].NewRow()
>4) ALLA FINE del for memorizza il DataSet nel database con myDataAdapter.Update(myDataSet)
>
>Quindi il problema è che non mi accorgo se ci sono righe duplicate
>finché non faccio l'update quindi al termine dell'inserimento
>dati...
>
>Come posso fare?

Perché devi fare tutto quel macello?
Non puoi semplicemente richiamare n volte una stored procedure analoga a quella che ti ho proposto ciclando i tuoi dati?
Purtroppo non è chiaro quello che stai facendo quindi non posso essere più preciso.

>Grazie ancora e grazie per il codice SQL che mi sarà sicuramente
>utile!
Il fatto è che quel codice contiene tutto quello che ti serve, lato client devi semplicemente fare un loop.

Ciao!

--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

Lindbergh Profilo | Newbie

>>Per come ho pensato e fatto il salvataggio dati, lo posso riassumere
>>in 4 punti:
>>
>>1) Carico l'intera tabella dal database in un DataSet;
>>2) for di tutte le nuove righe recuperate
>>3) nel for devo modificare la riga del DataSet se la nuova riga
>>ha la stessa descrizione e la stessa data altrimenti devo inserire
>>una nuova riga con: myDataSet.Tables["Tabella"].NewRow()
>>4) ALLA FINE del for memorizza il DataSet nel database con myDataAdapter.Update(myDataSet)
>>
>>Quindi il problema è che non mi accorgo se ci sono righe duplicate
>>finché non faccio l'update quindi al termine dell'inserimento
>>dati...
>>
>>Come posso fare?
>
>Perché devi fare tutto quel macello?
>Non puoi semplicemente richiamare n volte una stored procedure
>analoga a quella che ti ho proposto ciclando i tuoi dati?
>Purtroppo non è chiaro quello che stai facendo quindi non posso
>essere più preciso.

Hai ragione ma il problema è che per ora il database è Access quindi in questo caso secondo te dovrei aprire la connessione ed aggiornare riga per riga il database?
Così facendo non rischio di tenere il database aperto troppo a lungo e quindi, se qualche altro processo deve accedere ad altre tabelle lo trova occupato?

Quello che sto facendo è semplice da spiegare quindi lo posso riassumere in poche righe:
Ho un sistema che acquisisce dei dati da dispositivi esterni mediante le connessioni via modem GSM, i dati acquisiti dai dispositivi possono essere letti da utenti connessi al mio sito.
Quando ci sono le comunicazioni con questi dispositivi io devo memorizzare in tabelle diverse (una per ogni dispositivo che colloquia col mio software) i dati acquisiti.
Mentre c'è un dispositivo in linea oppure mentre sto memorizzando i dati, può essere che altri utenti nel frattempo leggano dati da altre tabelle quindi ho pensato di tenere il database occupato il meno possibile quindi avevo trovato come soluzione di caricarmi l'intera tabella, lavorarla e memorizzarla di nuovo dopo l'aggiornamento completo.

Concludo dicendo che, ovviamente, più utenti possono leggere nel database ma la scrittura è esclusivamente a carico del software che, con thread separati, acquisisce e memorizza i dati in modo automatico.

Spero di essere stato chiaro. ;-)


>
>>Grazie ancora e grazie per il codice SQL che mi sarà sicuramente
>>utile!
>Il fatto è che quel codice contiene tutto quello che ti serve,
>lato client devi semplicemente fare un loop.
>

Nel caso di un database SQL, le righe che mi hai scritto devono essere aggiunte al codice C# oppure sono da aggiungere come stored procedure in SQL?
Purtroppo questo ultimo passo è totalmente nuovo per me in quanto finora le dimensioni dei database mi hanno fatto lavorare solo con Access...


>Ciao!
>
>--
>Lorenzo Benaglia
>Microsoft MVP - SQL Server
>http://blogs.dotnethell.it/lorenzo/
>http://italy.mvps.org


Ciao
Stefano


Lindbergh Profilo | Newbie

HO TROVATOOO!!!
Era una stupidata, come spesso accade quando ci perdi un casino di tempo...
Se può interessare a qualcuno spiego la mia soluzione, ditemi cosa ne pensate!

public DataSet objMyDs = new DataSet();
// Carico la tabella intera in objMyDs...

// Nella tabella del DataSet aggiungo la chiave primaria alla colonna Descrizione e alla colonna DDHH
DataColumn[] keys = new DataColumn[2] {this.objMyDs.Tables["Table"].Columns["Descrizione"], this.objMyDs.Tables["Table"].Columns["DDHH"]};
this.objMyDs.Tables["Table"].PrimaryKey = keys;

// Recupero la nuova riga da inserire
public string desc;
public DateTime ddhh;
public int val;

// Metto in un array di due oggetti la descrizione e la data che non devono essere duplicate
object objValori = new object[2] {desc, ddhh};

// Cerco nella tabella la riga contenente le chiavi primarie specificate con i valori imposti in objValori
DataRow currentRow = this.objMyDs.Tables["Table"].Rows.Find(objValori);

// Se la riga non esiste ne aggiungo una nuova
if (currentRow == null)
currentRow = this.objMyDs.Tables["Table"].NewRow();

// Memorizzo i valori nella riga (o modifico la riga esistente)
currentRow["Descrizione"] = desc;
currentRow["DDHH"] = ddhh;
currentRow["Valore"] = val;

A questo punto se è un nuovo inserimento aggiungo la riga (this.objMyDs.Tables["Table"].Rows.Add(currentRow)) altrimenti aggiorno solo il database tramite il DataAdapter.

Ciao a tutti e alla prossima
Stefano
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2025
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5