2016-08-03 31 views
2

Eine zufällige Zeichenfolge (6 Zeichen) wird als Primärschlüssel in meiner SQL-Datenbanktabelle verwendet. Ich weiß jedoch nicht, wie der Fehler behandelt und die Abfrage erneut in die Datenbank eingefügt wird.Wie behandelt man Primärschlüssel Existenz für einen zufälligen String-Schlüssel in SQL?

Was ist die beste Lösung, die die SQL-Abfrage mit einem anderen zufälligen String-Schlüssel wieder in die Datenbank einfügt? (Tut den Wiedereinsatz in catch-Block?)

try 
      { 
       SqlConnection con = new SqlConnection(ConnectionString); 
       con.Open(); 
        var key = RandomString(6); 
        SqlCommand insertCommand = new SqlCommand("INSERT INTO tableName(d1, d2, d3) VALUES (@0, @1, @2)", con); 

        insertCommand.Parameters.Add(new SqlParameter("0", key)); 
        insertCommand.Parameters.Add(new SqlParameter("1", "values")); 
        insertCommand.Parameters.Add(new SqlParameter("2", DateTime.Now)); 
        var rowCount = insertCommand.ExecuteNonQuery(); 


       con.Close(); 
       if (rowCount < 1) 
       { 
        label.Text = "OMG it is Fail :("; 
        return false; 
       } 
       else 
       { 
        label.Text = "HEY ~~~ inserted"; 
        return true; 
       } 


      } 
      catch (Exception ex) 
      { 
       label.Text = ex.Message; 
       return false; 
      } 

Der obige Code ist in C# geschrieben, aber ich glaube nicht, das Problem C# ist nur. Daher ist es für eine andere Programmiersprache willkommen :)

+0

Wie wird der 'random' Schlüssel generiert? – Squirrel

+0

@Squirrel es ist eine Methode, die eine Zeichenfolge mit 6 Zeichen in 0-9 zurückgibt, a ~ z und A ~ Z –

+0

Anstatt zu versuchen, die Symptome eines schlechten Designs zu maskieren, wäre Ihre Zeit viel klüger ausgegeben ändern Primärschlüssel (der offensichtlich bedeutungslos ist, da er zufällig ist) zu etwas Sinnlichem wie einer Identität oder einer eindeutigen Identifizierung. Sie könnten sogar eine berechnete Spalte generieren eine 6-stellige Darstellung einer Identität, wenn Sie Rückwärtskompatibilität benötigen (auch nur Sechseck würde Ihnen 16,7 Millionen eindeutige Werte geben). –

Antwort

2

Haben Sie erwogen, eine SELECT-Abfrage zuerst mit der zufälligen Zeichenfolge auszuführen? Wenn das dann keine Ergebnisse zurückgibt, führen Sie den INSERT aus. Erstellen Sie andernfalls eine neue zufällige Zeichenfolge, bis keine Ergebnisse zurückgegeben werden.

===

Bearbeitet für Codebeispiel.

try 
{ 
    SqlConnection con = new SqlConnection(ConnectionString); 
    con.Open(); 
    int count = 1; 
    string key = string.Empty; 

    while (count > 0) 
    { 
     key = RandomString(6); 
     SqlCommand selectCommand = new SqlCommand("SELECT COUNT(*) FROM tableName WHERE d1 = @0", con); 
     selectCommand.Parameters.Add(new SqlParameter("0", key)); 
     count = (int)selectCommand.ExecuteScalar(); 
    } 

    SqlCommand insertCommand = new SqlCommand("INSERT INTO tableName(d1, d2, d3) VALUES (@0, @1, @2)", con); 

    insertCommand.Parameters.Add(new SqlParameter("0", key)); 
    insertCommand.Parameters.Add(new SqlParameter("1", "values")); 
    insertCommand.Parameters.Add(new SqlParameter("2", DateTime.Now)); 
    var rowCount = insertCommand.ExecuteNonQuery(); 


    con.Close(); 
    if (rowCount < 1) 
    { 
     label.Text = "OMG it is Fail :("; 
     return false; 
    } 
    else 
    { 
     label.Text = "HEY ~~~ inserted"; 
     return true; 
    } 
} 
catch (Exception ex) 
{ 
    label.Text = ex.Message; 
    return false; 
} 

===

ehrlich zu sein, das Verfahren oben, dass ich ziemlich schlecht bin zeigt. Es löst Ihr Problem von Kollisionen im Primärschlüssel, aber es ist nicht die richtige Weise, es zu tun. Ich würde eine der folgenden Änderungen empfehlen:

  • Ändern Sie den Primärschlüssel in der Tabelle in eine GUID. Dadurch können Sie Guid.NewGuid() für diesen Wert verwenden, von dem Sie annehmen können, dass er eindeutig ist.
  • Ändern Sie den Primärschlüssel in der Tabelle in eine automatisch inkrementierende Ganzzahl. Dann geben Sie im Code, der eingefügt wird, nicht einen Wert für diese Spalte ein. Dadurch kann die Datenbank den Primärschlüssel für Sie verarbeiten.
  • Wenn Sie müssen die zufällige Zeichenfolge als Primärschlüssel verwenden, würde ich eine Abfrage für die Tabelle für alle Primärschlüssel während der Initialisierung der Anwendung, und speichern Sie dieses Ergebnis in eine statische HashSet<string>. Wenn Sie dann eine neue zufällige Zeichenfolge in der RandomString(int)-Methode generieren, überprüfen Sie anhand dieses HashSet, ob es bereits existiert. Ist dies nicht der Fall, fahren Sie mit dem Einfügen fort, andernfalls generieren Sie einen neuen Eintrag.
+0

Das Problem liegt darin, dass der Fehler im catch-Block und nicht im try-Block ausgelöst wird. Ich habe nicht genug Fähigkeit, mit Block-To-Block-Kommunikation zu arbeiten ... –

+0

@kingyau Ich befürworte keine Arbeit mit irgendwelchen Fehlern. Ändern Sie Ihre Funktionalität, um eine SELECT-Abfrage für den Primärschlüssel Ihrer Tabelle mit der generierten zufälligen Zeichenfolge durchzuführen. Wenn es nicht existiert, führe deinen INSERT aus. –

+0

@kingyau - Ich habe ein Musterstück von Pseudo-Code für die Klarheit hinzugefügt. –

-1

Könnten Sie versuchen, die folgenden

Haben Sie den Try/Catch-Block in einer Schleife mit einer Flagge

die Flagge im Catch-Block Legen Sie die Schleife fortzusetzen, wenn es einen Fehler.

+0

Welche Schleife schlagen Sie vor? Ich weiß nur, wie man if-else und for loop verwendet. –

-2

Warum verwenden Sie nicht gespeicherte Prozedur?

So generieren Sie den zufälligen Schlüssel in der gespeicherten Prozedur (verwenden Sie etwas wie GUID, verwenden Sie die letzten 6 Zeichen oder so ähnlich).

Dann in der gleichen gespeicherten Prozedur, bevor Sie die ID einfügen, tun Sie eine Überprüfung zuerst, indem Sie die Anweisung exists. Wenn ID vorhanden ist, erzeuge ID erneut, wenn nicht, dann füge ein.

Ihr C# -Code wird nur diese gespeicherte Prozedur aufrufen. Eine Einfügung oder irgendeine SQL-Anweisung in C# zu machen ist schlecht, weil sie zur SQL-Injektion neigt.

+1

"Eine Einfügung oder irgendeine SQL-Anweisung in C# ist schlecht, weil es anfällig für SQL-Injektion ist" ist sowohl irreführend als auch falsch. SQL Injection tritt auf, wenn Sie Ihre Abfragen nicht parametrisieren. Da das OP dies im Codebeispiel richtig macht, hat Ihre Anweisung keine Bedeutung. In anderen Nachrichten ist SQL in C# weder schlecht noch gut, aber eine separate Möglichkeit, eine Lösung zu behandeln. –

3

Die Berechnung eines zufälligen Wertes und das Testen gegen die Datenbank ist eine langsame/nicht-skalierbare Möglichkeit, dies zu tun - es geht darum, einen verfügbaren Wert zu erraten. Je mehr Werte verwendet werden, desto schlechter ist die Wahrscheinlichkeit, einen verfügbaren Wert zu erraten, und desto langsamer wird der Prozess.

Wenn Sie wirklich nicht Identität oder Sequenz oder GUID dafür verwenden können, sah ich einen großen Vorschlag in einem anderen Thread, nur eine Tabelle mit einem "Pool" von eindeutigen Werten zu erstellen und sie zu verwenden. Wählen Sie jedes Mal, wenn Sie ein Objekt generieren, aus dieser Tabelle aus, und löschen Sie die Zeile, sodass der Wert nicht mehr verfügbar ist.

Achten Sie darauf, eine Transaktion korrekt zu verwenden, die andere gleichzeitige Leser daran hindert, den gleichen Wert zu erhalten.

+1

+1, aber Sie müssten die gesamte Operation in eine Transaktion umbrechen und die Transaktionsisolationsstufe auf serialisierbar setzen. –

+0

Benötigt definitiv eine Transaktion. Ich denke, mit Tablockx und Holdlock Tipps auswählen funktioniert auch. Grundsätzlich müssen das Lesen und das Löschen beide andere Leser blockieren. – onupdatecascade

+0

Bearbeitet, um das zu integrieren – onupdatecascade