Evitare dati duplicati in db

lunedì 18 luglio 2011 - 09.26

netting Profilo | Junior Member

Utilizzo un db sql server, e vorrei far in modo che all'inserimento dei dati in tabella venga controllato se quei dati sono già presenti (non singoli campi ma se tutti i campi sono identici), in questo caso segnalare tramite un messaggio l'errore.

Come è possibile realizzare questo?

Grazie

Gluck74 Profilo | Guru

come mai devi controllare proprio tutti i campi?? sicuro sia la strada giusta?

In teoria puoi farlo direttamente nel database:
Crei un campo varchar calcolato su tutte le colonne:
ALTER TABLE myTable ADD cs_row AS CHECKSUM(*); GO ALTER TABLE myTable ADD CONSTRAINT UK_cs_row UNIQUE NONCLUSTERED ( cs_Row ) WITH ( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY] GO

In questo modo hai una colonna che contiene un codice hash che rappresente tutta la riga
Ogni volta che inserisci una nuova riga, SQL ti restituisce un errore se stai inserendo dati uguali perché vai a violare la chiave univoca sul campo CheckSum.
Da codice devi solo controllare e gestire l'eventuale errore.


____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

ok ma il codice dove devo andare ad inserirlo?

Gluck74 Profilo | Guru

il codice che ti ho scritto serve per modificare la tabella, lo devi eseguire su SQL Server

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

si ma andando sulla tabella e facendo script as..alter to non è selezionabile, quindi in quale script as devo inserirlo?

Gluck74 Profilo | Guru

"NEW QUERY" in alto a sinistra!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

ok, ho scritto in questo modo
ALTER TABLE AnagraficaStagisti
ADD cs_row AS CHECKSUM(*);
GO
ALTER TABLE AnagraficaStagisti ADD CONSTRAINT
UK_cs_row UNIQUE NONCLUSTERED
(
cs_Row
) WITH
( STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]

GO

e mi dà questi errori:

Msg 1789, Level 16, State 1, Line 2
Cannot use CHECKSUM(*) in a computed column definition.
Msg 1911, Level 16, State 1, Line 1
Column name 'cs_Row' does not exist in the target table or view.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.

Scusa ma sono alle prime armi con queste applicazioni.

Gluck74 Profilo | Guru

per caso nella tabella hai dei campi di questo tipo?
text, ntext, image, XML, cursor, sql_variant

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

i miei campi sono:
(<Nome, nchar(30),>
,<Cognome, nchar(30),>
,<DataNascita, nchar(30),>
,<LuogoNascita, nchar(30),>
,<Ruolo, nchar(40),>)
e poi ovviamente l'ID che è IDStagisti di tipo intero

Gluck74 Profilo | Guru

visto che i campi sono pochi, mettili allora tutti, separati da virgola, al posto di "*".

non dovrebbe più darti errore

CHECKSUM(campo1, campo2, ....)

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

ho scritto tutti i campi in questo modo:

ALTER TABLE AnagraficaStagisti ADD cs_row AS CHECKSUM(Nome, Cognome, DataNascita, LuogoNascita, Ruolo, Utente, Password); GO ALTER TABLE AnagraficaStagisti ADD CONSTRAINT UK_cs_row UNIQUE NONCLUSTERED ( cs_Row ) WITH ( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY] GO

e mi dà questo errore:

Msg 1505, Level 16, State 1, Line 1
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.AnagraficaStagisti' and the index name 'UK_cs_row'. The duplicate key value is (1541097578).
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.
The statement has been terminated.

Gluck74 Profilo | Guru

controlla se hai gia nel database una riga duplicata

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

e si ci sono già righe duplicate

Gluck74 Profilo | Guru

vai tranquillo che il CHECKSUM non sbaglia!!!!!!!!!!!!!!!!!!!!

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

e quindi ora da codice come gestisco questo risulatato?
Nel senso con il c# come interpreto il risultato dinamicamente della query?

Quando premo il pulsante quindi devo controllare se ci sono già righe duplicate in caso negativo inserire i dati nel db altrimenti visualizzare un messaggio e dire che i dati sono già presenti

Gluck74 Profilo | Guru

//codice per la connessione //codice per il command string query = "Insert .. ... .."; try { objcommand.ExecuteNonQuery(); } catch (Exception err) { string messaggio = err.Message; } finally { //chiudi tutto }

In questo modo, quando stai inserendo una riga che esiste già, sarà direttamente il database a dirti che stai violando il vincolo che hai appena creato.
Controlla il messaggio di errore e crea un caso particolare per il tuo vincolo UK_cs_row

Ciao

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

Questo il codice:
//Codice Connessione

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

ma mi dà errore dicendo : use of unassigned local variable 'comando'.
inoltre in finally consa devo mettere solo la chiusura del db (cn.close();) ?

Gluck74 Profilo | Guru

devi fare un minimo di mente locale, mi pare che stai copiando il codice a casaccio.

string connString = "--- ---"; string sqlQuery = "--- ---"; using (SqlConnection conn = new SqlConnection(connString)) { using (SqlCommand cmdInsert = new SqlCommand(sqlQuery, conn)) { try { conn.Open(); cmdInsert.ExecuteNonQuery(); } catch (SqlException sqlerr) { //controlla l'errore restituito da SQL Server } catch (Exception err) { //gestisci l'errore generico } } }

http://authors.aspalliance.com/quickstart/aspplus/doc/webdataaccess.aspx
http://msdn.microsoft.com/en-us/library/6759sth4.aspx


____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

Allora ho fatto una modifica:
Ora ho due tabelle di uno stesso database, una per registrare dei campi generali (nome, cognome, datanascita ecc..) l'altra dove inserisco i dati user e password per il login.
Ho fatto due new query che mi hai suggerito in precedenza una per una tabella(anagraficastagisti) un'altra per la tabella login.
Quindi gestisco tramite codice c# l'eventuale errore di dati duplicati ma così facendo ricevo l'errore solo sulla prima tabella(anagraficastagisti) mentre anche se inserisco dati già presenti nella tabella login li inserisce comunque.

Posto il codice per la gestione della prima tabella.
Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

Questo il codice della seconda tabella (login):

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

Quindi con questo codice il funzionamento è corretto solo riguardo la prima tabella, mentre per la tabella login i dati anche se duplicati vengono inseriti.

spero di essere stato abbasanza esauriente nella spiegazione.
Dove sbaglio?

Gluck74 Profilo | Guru

be certo, il checksum funziona sulla tabella dove l'hai creato!!!!!!!!!!!!!!!!!!!
è normale che nell'altra non funzioni. Dovresti fare la stessa procedura anche su quella tabella

poi ti consiglio di gestire meglio l'errore, per evitare di dare messaggi errati all'utente. Se hai un effore generico, per qualsiasi motivo, il risultato che hai ora è comunicare al cliente di cambiare i dati, anche se l'errore è un altro.
gestisci meglio, come ti ho consigliato, con 2 istruzioni catch, inoltre fai sempre e comunque, dentro il catch, il controllo del tipo di errore e del messaggio.

Inoltre devi considerare che se va in errore una tabella, devo interrompere le operazioni anche nell'altra. Per fare questo devi usare una transazione:

bool tuttoOK = true; using (TransactionScope scope = new TransactionScope()) { string connString = "--- ---"; string sqlQuery = "--- ---"; using (SqlConnection conn = new SqlConnection(connString)) { using (SqlCommand cmdInsert = new SqlCommand(sqlQuery, conn)) { try { conn.Open(); cmdInsert.ExecuteNonQuery(); } catch (SqlException sqlerr) { //controlla l'errore restituito da SQL Server tuttoOK = false; } catch (Exception err) { //gestisci l'errore generico tuttoOK = false; } } } //idem per l'altra tabella if (tuttoOK) scope.Complete(); }

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

si capisco la gestione degli errori devo migliorarla, ma la stessa query per l'altra tabella l'ho fatta ma non funziona come per l'altra tabella e mi salva i dati anche duplicati.

Questo è il codice per l'altra tabella:

ALTER TABLE Login ADD cs_row AS CHECKSUM(Utente, Password); GO ALTER TABLE Login ADD CONSTRAINT UK_cs_row UNIQUE NONCLUSTERED ( cs_Row ) WITH ( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY] GO

e mi dà quuesti errori:

Msg 2714, Level 16, State 4, Line 1
There is already an object named 'UK_cs_row' in the database.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.

Gluck74 Profilo | Guru

> There is already an object named 'UK_cs_row' in the database.

mi sembra abbastanza chiaro......
Cambia nome al vincolo, chiamalo magari UK_Login_cs_row

____________
Ricordati di utilizzare il tasto "Accetta" se i nostri consigli ti sono serviti a risolvere il problema.
È il modo per ringraziare chi ti ha aiutato.

netting Profilo | Junior Member

Grazie! Funziona
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-2017
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5