Laden...

Letzte belegte / nächste freie ID ermitteln

Erstellt von rebbi vor 15 Jahren Letzter Beitrag vor 15 Jahren 11.994 Views
R
rebbi Themenstarter:in
49 Beiträge seit 2008
vor 15 Jahren
Letzte belegte / nächste freie ID ermitteln

[EDIT=herbivore]Abgeteilt von Zahl erhöhen (Sonderformat?)[EDIT]

Grüß Euch,

das nächste Problem, bei dem ich häng:

Ich muss meine ID (String, bestehend aus DateTime und Counter, also im Format yymmddxxxx) bei jedem neuen Eintrag erhöhen, AutoIncrement geht natürlich aufgrund des String-Typs nicht.

Wie les ich jetzt aus der Datenbank immer den letzten Wert aus?

Bisher hab ich das per DataTable geregelt, aber auf die Dauer ist die Performance zu schlecht, wenn ich jedesmal nur zum auslesen der letzten ID die komplette DataTable lokal speichern muss ...

Hab schon

  • "SELECT LAST(Identnumber) FROM table1"
  • "SELECT MAX (Identnumber) FROM table1"

probiert. Funktionierten nicht.

  • cmd.LastInsertedID gibt mir 0 zurück, was beim erstmaligen Start meiner Anwendung auch logisch ist. Nun soll mein Programm zwar im Dauerbetrieb laufen, aber wenns doch mal neugestartet werden muss wären die folgenden IDs ja wieder falsch.

  • "SELECT COUNT(*) FROM table1" bringt auch nix, da ich dann zwar die Anzahl der Datensätze habe, aber in der Where-Klausel eben die ID angeben müsste, die ich ja suche. 😁

Mein allerletzter Ansatz wäre, eine extra AutoIncrement-Spalte in die Datenbank mit aufzunehmen und dann über diese die Daten anzusteuern ... ist aber bisschen umständlich, desweiteren hätte ich dann in einer Tabelle 2 Primary Keys ("meine" ID sowie die AI-ID). Das ist sicher auch nicht das allerbeste, soferns überhaupt geht.

Weiß einer Rat? 🙂

mfG Andi

4.221 Beiträge seit 2005
vor 15 Jahren

SELECT TOP1 FROM.... ORDER BY ... DESC wäre wohl geschickter als die ganze Tabelle auszulesen...

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

R
rebbi Themenstarter:in
49 Beiträge seit 2008
vor 15 Jahren

Bin selbst drauf gekommen, funktioniert wunderbar:

string strSelectLastID = "SELECT COUNT(*) FROM table1";
            MySqlCommand cmdSelect = new MySqlCommand(strSelectLastID, conn);
            conn.Open();            
            int a = Convert.ToInt32(cmdSelect.ExecuteScalar())-1;
            string strSelectLastID2 = "SELECT Identnumber FROM table1 LIMIT " + a + ",1";
            MySqlCommand cmdSelect2 = new MySqlCommand(strSelectLastID2, conn);
            string erg = Convert.ToString(cmdSelect2.ExecuteScalar());
            string strSQL = "INSERT INTO table1 (Identnumber, Timestamp, Type, PRG_NR, PRG_RUNTIME, MIXINGRATIO, BOOTH_TEMPERATURE," +
                " BOOTH_HUMIDITY, ERROR) Values (?id, ?ts, ?type, ?nr, ?runtime, ?ratio, ?temp, ?humidity, ?error)";
            MySqlCommand cmd = new MySqlCommand(strSQL, conn);            
            string datetime = DateTime.Now.ToString("yyMMdd");
            string ergChanged = erg.Substring(6, 4);
            string id = datetime + (int.Parse(ergChanged) + 1).ToString(@"0000"); 
            conn.Close();           

Zuerst hol ich die Gesamtanzahl der Datensätze und zieh eins ab. Mit Hilfe des LIMIT-Befehls kann ich dann den letzten Eintrag auswählen.
Ein simpler Befehl, den ich aber nicht kannte bzw. durch Zufall nach ewigem Suchen in einem Flashforum gefunden hatte ... 🙂

H
208 Beiträge seit 2008
vor 15 Jahren

Sowohl für solche "Sonder"-IDs wie in Deinem Beispiel als auch für fortlaufende Zahlen für die sich ein normaler Autoincrement trotzdem nicht eignet (in meinem Fall: Aufträge mit verschiedenen Nummernkreisen die jeweils in sich fortlaufend sein müssen, aber alle zusammen in einer Tabelle stehen weil sie identisch aufgebaut sind) benutze ich immer eine separate "Zählertabelle":

2 Spalten, eine für den Typ (Primärschlüssel) und eine für die ID.
Dazu eine Funktion die den Typ übergeben bekommt (damit man mehrere verschiedene Zähler in der gleichen Tabelle speichern kann) und dann folgendes macht:*den einen Datensatz mit diesem Typ suchen *ID in der Tabelle um 1 erhöhen *diese gerade veränderte ID lesen & zurückgeben

Das ist auf jeden Fall nochmal eine ganze Ecke schneller als Deine Lösung weil diese Zählertabelle im besten Fall nur einen einzigen, maximal einige wenige Datensätze enthält (im Gegensatz zu der stetig steigenden Anzahl Datensätze in Deiner eigentlichen Datentabelle).

Außerdem (aber das ist nur ein netter Nebeneffekt, der erste Vorteil ist der gravierendere) sparst Du Dir den Aufwand, die fortlaufende Nummer erstmal aus dem ganzen Schlüssel extrahieren zu müssen.

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo Rebbi,
eventuell wäre auch Nächste Id herausfinden was fü dich

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

H
208 Beiträge seit 2008
vor 15 Jahren

Das ist ja genau das, was ich gesagt habe...nur halt mit fertigem Codebeispiel 😁

R
rebbi Themenstarter:in
49 Beiträge seit 2008
vor 15 Jahren

Hey, die Antworten hier hab ich garnicht mehr bemerkt. Danke! 👍

Werd mir deine/eure Lösung jetzt mal anschauen, seit heute hab ich nämlich ein neues Problem:

verwendetes Datenbanksystem: <MySQL>

Folgendes Codestück liest mir meine letzte vergebene ID (yymmddxxxx) aus und ermittelt so die ID, die ich als nächstes in die Datenbank schreiben muss:

public string getActualID()  
        {  
            try  
            {  
                MySqlConnection conn = new MySqlConnection("DATA SOURCE=" + Infos.databaseIP + ";DATABASE= " +  
                    Infos.database + ";UID=" + Infos.uid + ";PASSWORD=" + Infos.password + "; pooling = false;");  
                string counter;  
                string strSelectCount = "SELECT COUNT(*) FROM " + Infos.tablename1 + ""; // Holen der Gesamtzahl der Einträge  
                MySqlCommand cmdSelectCount = new MySqlCommand(strSelectCount, conn);  
                conn.Open();  
                int a = Convert.ToInt32(cmdSelectCount.ExecuteScalar()) -1;  // Auswählen des letzten Eintrags  
                string strSelectLastID = "SELECT " + Infos.id + " FROM " + Infos.tablename1 + " LIMIT " + a + ",1";  
                MySqlCommand cmdSelectLastID = new MySqlCommand(strSelectLastID, conn);  
                string lastID = Convert.ToString(cmdSelectLastID.ExecuteScalar()); // Holen des letzten Eintrags  
                conn.Close();                
                string datetime = DateTime.Now.ToString("yyMMdd");  
                int date = int.Parse(lastID.Substring(4, 2)); // Datum des letzten Eintrages  
                int today = DateTime.Now.Day;  
                if (!date.Equals(today))  
                {  
                    counter = 0.ToString(@"0000");  
                    DateHasChanged.Invoke();  
                }  
                else  
                {  
                    counter = lastID.Substring(6, 4);  
                }  
                string id = datetime + (int.Parse(counter) + 1).ToString(@"0000");  
                return id;  
            }  
            catch  
            {  
                MessageBox.Show(get_text("Error! SQL-Server may be down! Please restart SQL-Server and the Watchdog!"));                  
                Thread.CurrentThread.Abort();  
                return "-1";  
            }  
        }  

Das hat bisher auch super hingehauen. Jetzt auf einmal liest er aber die letzte ID nicht mehr richtig aus.

Er liest 1mal oder 2mal die ID richtig aus und vergibt so die neue richtig. Dann aber "ignoriert" er auf einmal den letzten Eintrag und bekommt bei

string lastID = Convert.ToString(cmdSelectLastID.ExecuteScalar());  

auf einmal den vorletzten Eintrag raus. Und ich hab keine Ahnung wieso 🤔

Hab eigentlich nicht viel geändert. Nur z.B. n Foreign Key hinzugefügt oder 1-2 neue Methoden eingebaut, was aber alles eigentlich nix mit dieser Methode zu tun hat und auch nur lesend auf die Datenbank zugreift.

Hat vielleicht von euch einer den Blick und findet das Problem? _

Einer ne Idee? 🙂

R
rebbi Themenstarter:in
49 Beiträge seit 2008
vor 15 Jahren

Habs gelöst und poste das für nachfolgende User wieder hier. Die neue Lösung müsste normalerweise auch das Problem umgehen, welches haarrrgh angesprochen hat (hätte wohl auf die Dauer wirklich zu langen Ladezeiten geführt).

Hab einfach die Routine


string strSelectCount = "SELECT COUNT(*) FROM " + Infos.tablename1 + ""; // Holen der Gesamtzahl der Einträge
                MySqlCommand cmdSelectCount = new MySqlCommand(strSelectCount, conn);
                conn.Open();
                int a = Convert.ToInt32(cmdSelectCount.ExecuteScalar()) -1;  // Auswählen des letzten Eintrags
                string strSelectLastID = "SELECT " + Infos.id + " FROM " + Infos.tablename1 + " LIMIT " + a + ",1";
                MySqlCommand cmdSelectLastID = new MySqlCommand(strSelectLastID, conn);
                string lastID = Convert.ToString(cmdSelectLastID.ExecuteScalar()); // Holen des letzten Eintrags
                conn.Close();

durch


string strSelectCount = "SELECT MAX(" + Infos.id + ") FROM " + Infos.tablename1 + "";
MySqlCommand cmdSelectCount = new MySqlCommand(strSelectCount, conn);
conn.Open();
string lastID = Convert.ToString(cmdSelectCount.ExecuteScalar()); // Holen des letzten Eintrags
conn.Close();

ersetzt.

Zu zeiten meines Eröffnungspost in diesem Thread ging MAX(id) noch nicht - jetzt tuts.

Funktioniert (bisher) reibungslos.

H
208 Beiträge seit 2008
vor 15 Jahren

Naja, das umgeht aber nicht wirklich das Problem das ich angesprochen habe.

Du hast ja im Prinzip nur Count() durch Max() ersetzt.
Du mußt aber trotzdem jedes Mal erst einen Zugriff auf Deine Tabelle mit den Echtdaten machen (die in 1 oder 2 Jahren ggf. ganz schön groß sein wird) und Du mußt trotzdem noch den String auseinanderhacken um die eigentliche ID daraus zu bekommen.

Mit meinem Lösungsvorschlag hättest Du das beides nicht, sondern Du mußt nur einen Lese- und Schreibzugriff auf eine winzige Tabelle machen (die aus 2 Spalten und im Idealfall nur einer einzigen Zeile besteht) und hast dann direkt die richtige ID, ohne sie vorher aus einem String extrahieren zu müssen.

R
rebbi Themenstarter:in
49 Beiträge seit 2008
vor 15 Jahren

Mhh.

Das heißt, dass ich in 1-2 Jahren sehr lange für meinen MAX-Zugriff brauch?

Dachte bei MAX wertet er nur den letzten Wert aus, anstatt die ganze Tabelle zu durchlaufen. Hab ich mich da getäuscht?

mfG Andi

H
208 Beiträge seit 2008
vor 15 Jahren

Wenn die Spalte indiziert ist, dann holt er sich den letzten Wert aus dem Index und durchläuft NICHT die ganze Tabelle. Das wäre also kein Problem.

Das dauert dann auch nicht "sehr lange", sondern trotzdem nur Sekundenbruchteile (habe gerade bei mir einen Test gemacht: MSSQL 2005, 7.5 Mio Datensätze und trotzdem rasend schnell, eben durch den Index).

Wenn viele Benutzer gleichzeitig in Deine Tabelle schreiben könnte es höchstens passieren daß 2 sich gleichzeitig den gleichen Max-Wert holen, die gleiche neue ID ermitteln und der, der erst als zweiter versucht zu schreiben eine Schlüsselverletzung bekommt.

Wenn Du aber nur ein paar Benutzer hast und/oder nicht so oft in die Tabelle geschrieben wird, dann wird Deine Lösung so wie sie jetzt ist genausogut und ohne Geschwindigkeitsprobleme funktionieren.