Kann ich gleichzeitig einen Wert ändern und auslesen (SQL)?
Ich möchte eine Tabelle erstellen in die verschiedene Werte eingetragen werden sollen. Darunter gibt es eine Spalte mit dem Namen "ID". Diese ID soll immer eins höher als die zuletzt hinzugefügte ID sein. Da IDs aber auch wieder gelöscht werden können, habe ich mir überlegt dass man ein Eintrag erstellt welcher die ID 0 besitzt
Die Tabelle besitzt noch eine zweite Zeile namens "Content". Wenn man jetzt einen neuen Eintrag macht, soll der Content des Eintrages mit der ID 0 um 1 erhöht werden, außerdem soll der neue Eintrag als ID den neuen wert des Contents bekommen:
Tabelle vorher
ID| Content
0 | 10
Tabelle nacher
ID| Content
0 | 11
11| "Ich mag Kekse"
Da dieser Code auch funktionieren soll, wenn viele Leute gleichzeitig neue Einträge erstellen, kommt das UPDATE von mySQL ganz gelegen:
UPDATE table_name SET content =content+1 WHERE ID=0
Hiermit wird schonmal verhindert, dass wenn zwei Leute gleichzeitig einen Eintrag erstellen, die ID nur um 1 erhöht wird.
Jetzt soll aber gleichzeitig der content bei ID=0 ausgelesen werden. Wenn man die anweisungen nacheinander ausliest kann es sein, dass benutzer 1 den wert bekommt der durch benutzer 2 entstanden ist (wenn sie gleichzeitig einen neuen Eintrag machen). (zumindest wenn man nach dem oben genannten code den Content mit SELECT ausließt.)
Wie kann man also mit MySQL in einer Zeile einen Wert ändern und gleichzeitig auslesen? in diesem Fall soll der Content bei ID=0 um eins erhöht werden und gleichzeitig soll der gleiche Content ausgelesen werden. (Dabei ist es egal ob man den Wert von vor der Addition oder den von danach bekommt, da man ja dann noch +1 rechnen kann :D )
3 Antworten
Oh je! - Das 'ID' Problem ist doch ein absoluter Klassiker.
Ich verstehe dass dich das Problem beschäftigt, und ich weiß ehrlich gesagt auch gar nicht ob das schon jemand komplett gelöst hat. Jedoch habe ich mich mit dem Thema genauso auseinander gesetzt wie du, daher glaube ich genau zu verstehen was dein Ziel ist.
Du möchtest in der ID Spalte keinen Autowert vom SQL-Server vergeben lassen? Ich habe diverse Vermutungen, aber ich würde mich freuen wenn du mir das bestätigen könntest, oder mir einen Grund dafür nennen könntest.
Das Prinzip was du hier versuchst, ist eine Mischung aus dem Prinzip einer TAN-Tabelle und der MAX(ID) Variante. (Zwei unterschiedliche Ansätze).
Du hast mit deiner aktuellen Variante ein Problem, nämlich: Willst du so bei anderen Tabellen vorgehen, benötigst du immer eine Content-Spalte. Der Datentyp von Content und ID wird unterschiedlich sein. Daher erstelle doch dafür eine Tabelle, in der Tabellenname deiner Zieltabelle steht, sowie die zuletzt vergebene ID.
Ich weiß, das löst noch nicht dein Problem. Sondern macht es nur 'sauberer'. Du hast nämlich nun immer noch das Problem, dass es dadurch noch nicht wirklich Mehrbenutzer-fähig ist. - Was das betrifft, hätte ich noch folgenden Ansatz: Transaktionen:
Bitte verzeih wenn ich einen Mist erzähle, eigentlich ist die Syntax in MySQL und MSSQL in vielen Dingen gleich und ich bin nicht ganz so fit in MySQL.
In MSSQL habe ich die Möglichkeit dafür eine Procedure zu bauen: Ein komplexeres SQL-Statement dass ich in eine Funktion packen kann, die ich später simpel aufrufen kann.
In dieser Funktion benötigst du eine Transaktion, die zunächst den Wert der TAN-Tabelle Abfrägt, um eins nach oben zählt und wieder in die Tabelle zurück schreibt und am Ende der Funktion dem Anwender die ID übermittelt.
Ohne Stored Procedure könnte das so aussehen:
DECLARE @ID int; BEGIN TRANSACTION SELECT @ID=TableID FROM MyTanTable WHERE TableName = 'table_name' SET @ID = @ID+1; UPDATE MyTanTable SET TableID=@ID WHERE TableName = 'table_name' COMMIT TRANSACTION INSERT INTO table_name(ID, Content) SELECT @ID, 'mein neuer inhalt'
Aus der Praxis noch folgender Hinweis: Du hast ein kleines aber feines Problem, dem du mit einem Workaround begegnen musst: Auch wenn du eine Transaktion startest, löst Das Select-Statement noch keine Datensatz-Sperre aus. wodurch ein Paralleler-Zugriff auf die Transaktion möglich ist. - Daher wirst du noch ein weiteres Feld in der TAN-Tabelle benötigen, das nennst du einfach: Lock als smallint.
Direkt nach der Zeile mit BEGIN TRANSACTION setzt du die Sperre via:
UPDATE MyTabTable SET Lock=-1 WHERE TableName = 'table_name'
und setzt beim zweiten UPDATE-Statement, in der du die neue ID zurück schreibst das Feld wieder auf 0.
Ich verstehe ehrlich gesagt nicht was du da genau machst :-)
Du kannst den primary-key ID doch auf auto-increment stellen. Dann brauchst du dich um deine ID nicht kümmern, er inkrementiert dann den Index automatisch.
Bei einem INSERT liefert er dir die ID normalerweise zurück, so dass du diesen Wert wieder lesen kannst. Wenn du ohnehin schon etwas einfügst, kennst du den Inhalt bereits. --> Brauchst dir diese Mühe grundsätzlich nicht machen.
Ich weiß schon was ein TAN-Verfahren ist. Mir kam die Anfrage seitens des Fragestellers aber eher .. forschend rüber. :-)
Kein Problem. - Ich finde das Thema im allgemeinen schwierig. Und ich nehme an, der Fragesteller wusste auch einfach noch nicht den entsprechenden Begriff für =)
Das kommt mir alles ziemlich sinnlos und unüberlegt vor. Warum nimmst du nicht einfach die ID als Primärschlüssel und setzt diese Spalte auf autoincrement. Dann musst du nix an den IDs ändern.
Ein Grund weshalb man ein TAN-Verfahren gegenüber Autoincrement verwendet, wäre, wenn man in der Applikation eine komplette Relation abbilden möchte, bevor man diese Speichert, z.B.:
Rechnung: ID 1
Position ID 1, RechnungsReferenz auf RechnungID.