# Tabellenentwurf und synchronized SQL-Block?



## Leroy42 (19. Apr 2006)

Ich möchte eine Datenbank für Bestellungen anlegen. Da jeder Kunde, im Laufe der Zeit,
mehrere Bestellungen tätigen kann und bei jeder Bestellung mehrere Produkte bestellt
werden können, möchte ich euch fragen, ob mein Tabellenentwurf vernünftig ist.

1. Tabelle Kunden
id : auto_increment primary key
"Daten des Kunden"

2. Tabelle Bestellungen
id : auto_increment primary key
kunde : id aus Kunden
"Daten der Bestellung wie bestelldatum, lieferdatum, kundenkommentar, ..."

3. Tabelle Bestellung
id : auto_increment primary key
bestellung : id aus Bestellungen
produkt: tinyint
anzahl: mediumint
preis: mediumint

Also die Tabelle _Bestellung_ enthält pro Zeile wie oft welches Produkt
zu welchem Gesamt(Rabatt, ...)preis bestellt wurde. Über _bestellung_
kommt man auf den zugehörigen Datensatz der Bestellungen-Tabelle.

Die Tabelle _Bestellungen_ enthält pro Zeile *eine* Bestellung und
verweist auf den Kunden.

Macht das so Sinn, oder liege ich da fallig völsch?  :shock: 

Nachdem ich mir ein wenig Gedanken gemacht habe, bin ich auf ein
potentielles Synchronisationsproblem gestoßen.

Annahme: Zwei Kunden geben via Browser _relativ gleichzeitig_ eine Bestellung auf:
1. JSP-Instanz 1 fügt Kunde1 in _Kunden_ ein.
2. JSP-Instanz 2 Server fügt Kunde2 in _Kunden_ ein.
3. JSP-Instanz *1* fragt via "SELECT MAX(id) FROM Kunden" nach der id
um diese in die Tabelle _Bestellungen_ einzutragen, aber erhält id von Kunde_2_
...
Ergebnis: In der Datenbank hat Kunde1 nix bestellt und für Kunde2 sind beide Bestellungen  :shock: 

Darum die Frage: Gibt es in SQL so etwas wie in Java den synchronized-Block?
So daß ich SQL sagen kann, er soll erst *alle* SQL-Befehle *einer* Bestellung
ausführen bevor der nächste Kunde _reindarf_.

Oder muß ich das irgendwie mit Java lösen?


----------



## Sanix (19. Apr 2006)

Ich nehme an, dass du eine mysql Datenbank hast? Dort gibt es das. Schau auf mysql.com in der Dokumentation nach. Du kannst entweder die Tabellen sperren oder alle Befehle auf einmal oder gar nicht ausführen lassen.

Was ich an deinem Tabellenentwurf nicht ganz begreifen. Wofür die Tabelle Bestellungen? Du könntest doch alles in die Tabelle Bestllung schreiben.
Danach kriegst du mit einfachen SQL Statements locker die gewünschten Daten.

Bestellung
id_n
kunde_id_n
produkt_id_n
anzahl_n
preis_n
etc.

3. JSP-Instanz 1 fragt via "SELECT MAX(id) FROM Kunden" nach der id 
In php gibts nen Befehl mysql_insert_lastid oder so. Vielleicht gibts dies in Java auch. Zudem lässt sich das Problem viel einfacher umgehen wenn du
select id from Kunden ku where ku.name_s = 'bla'


/edit
Die Tabelle Tabelle bräuchte eigentlich keine id_n, kommt draufan, ob du sie weiter verwenden willst.


----------



## Caffè Latte (19. Apr 2006)

Hi,

zu auto_increment: LAST_INSERT_ID gibt es genauso unter Java, da das von MySQL kommt:


```
rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
```

Das ist auch Thread-sicher.


----------



## Leroy42 (19. Apr 2006)

Sanix hat gesagt.:
			
		

> Wofür die Tabelle Bestellungen? Du könntest doch alles in die Tabelle Bestllung schreiben.
> Danach kriegst du mit einfachen SQL Statements locker die gewünschten Daten.


Schon, aber müßte ich dann nicht alle Informationen zu einer Bestellung (z.b. die 2. Bestellung eines
Kunden an einem anderen Tag) wie _Bestelldatum, Bezahlung eingegangen, Ware geliefert_ und Bestellkommentare wie
_"Bitte liefert die Waren an die Adresse meines Urlaubsflirts: ....
und schickt mir keine Rechnung nach Hause, damit mein Ehemann nix mitbekommt"
_
ja bei jedem einzelnen Artikel mit eintragen, um die Zuordnung nicht zu verlieren ?  :shock: 



			
				Sanix hat gesagt.:
			
		

> Zudem lässt sich das Problem viel einfacher umgehen wenn du
> select id from Kunden ku where ku.name_s = 'bla'


Aber wie bekomme ich dann die Eindeutigkeit hin, ohne alle Attribute in der Abfrage zu verwenden?
Gerade in deinem Beispiel sieht man doch, daß der Nachname alleine kein eineindeutiges Kriterium ist.
Auch der _gesamte_ Name muß dies nicht unbedingt sein (Schon öfter vorgekommen).


			
				Sanix hat gesagt.:
			
		

> Die Tabelle Tabelle bräuchte eigentlich keine id_n, kommt draufan, ob du sie weiter verwenden willst.


Bin halt ein ordentlicher Mensch


----------



## AlArenal (19. Apr 2006)

Leroy42 hat gesagt.:
			
		

> Darum die Frage: Gibt es in SQL so etwas wie in Java den synchronized-Block?
> So daß ich SQL sagen kann, er soll erst *alle* SQL-Befehle *einer* Bestellung
> ausführen bevor der nächste Kunde _reindarf_.



Das Zauberwort lautet: Transaktion

Bei MySQL brauchts dafür aber Tabellen in einem DB-internen Tabellenformat, welches Transaktionen unterstützt, wie z.B. InnoDB. DIe standardmäßig benutzten MyISAM-Tabellen unterstützen keine Transaktionen.


----------



## Leroy42 (19. Apr 2006)

Caffè Latte hat gesagt.:
			
		

> ```
> rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
> ```
> Das ist auch Thread-sicher.


Wie darf ich das verstehen? 

Wenn also 2 _Java_-Threads am werkeln sind, bekommt dann jeder Thread
mit LAST_INSERT_ID() dann auch tatsächlich die letzte ID _seines eigenen_ Inserts?
Woher weiß die DB das denn? Merkt sie sich alle IDs per Statement oder per Connection?

Hieße das nicht, das ich _keine_ statische connection/statement Verbindung
während der Laufzeit des Servlet-Wrappers (oder wie auch immer das heißt)
aufrechterhalten darf, sondern mich _jedesmal_ neu verbinden muß?
Ist das nicht zu resourcenaufwendig?



			
				Caffè Latte hat gesagt.:
			
		

> LAST_INSERT_ID gibt es genauso unter Java, da das von MySQL kommt


Von mySQL oder von SQL selbst? Dann müßte ich ja sicher sein, daß auch ein mySQL-Server läuft


----------



## Leroy42 (19. Apr 2006)

AlArenal hat gesagt.:
			
		

> ...welches Transaktionen unterstützt, wie z.B. InnoDB. DIe standardmäßig benutzten MyISAM-Tabellen unterstützen keine Transaktionen.



Aha, jetzt weiß ich auch was dieser Kram soll, den ich bei phpMyAdmin einstellen kann. 

Aber vielleicht erübrigt sich das, wenn ich _Caffè Latte_ richtig interpretiere.

Kannst du mir etwas zu _Sanix_ Vorschlag, auf die Bestellung*en*-Tabelle zu verzichten
etwas sagen? Hat er doch Recht?


----------



## AlArenal (19. Apr 2006)

Leroy42 hat gesagt.:
			
		

> Kannst du mir etwas zu _Sanix_ Vorschlag, auf die Bestellung*en*-Tabelle zu verzichten
> etwas sagen? Hat er doch Recht?



Hat er nicht, lediglich die Namensgebung deiner Tabellen (Bestellung, Bestellungen) ist etwas unglücklich, abgesehen davon dass ich Englisch bevorzuge.

Soweit ich das verstehe befindet sich in "Kunden" pro Zeile alle Infos über einen Kunden, "Bestellungen" erhält pro Zeile alle spezifischen Infos einer einzelnen Bestellung und in "Bestellung" (ich würde es umbenennen in "Posten" oder "Positionen") stehen dann pro Zeile je ein Posten, wie er auf einer zugeordneten Bestellung steht.

Die Beziehungen sind soweit auch klar. Ich kann jedem Kunden seine Bestellungen und Posten zuordnen, ich kann jeder Bestellung einen Kunden und die zugehörigen Posten zuordnen und ich kann zu jedem Posten ruasfinden zu welcher Bestellung er gehört. Sieht doch prima aus


----------



## Caffè Latte (19. Apr 2006)

Hi,



			
				Leroy42 hat gesagt.:
			
		

> Wenn also 2 _Java_-Threads am werkeln sind, bekommt dann jeder Thread
> mit LAST_INSERT_ID() dann auch tatsächlich die letzte ID _seines eigenen_ Inserts?
> Woher weiß die DB das denn? Merkt sie sich alle IDs per Statement oder per Connection?



Das haben die Entwickler des JDBC-Treibers eben "so da rein programmiert". Es wird die ID des letzten Inserts zurückgegeben. Daher ist die Anfrage gleich nach dem Insert abzusetzen (und nicht nach einem weiteren Insert auf eine Tabelle mit einem auto_increment-Feld)



			
				Leroy42 hat gesagt.:
			
		

> Hieße das nicht, das ich _keine_ statische connection/statement Verbindung
> während der Laufzeit des Servlet-Wrappers (oder wie auch immer das heißt)
> aufrechterhalten darf, sondern mich _jedesmal_ neu verbinden muß?
> Ist das nicht zu resourcenaufwendig?



Was meinst du mit jedes mal neu verbinden? Nach dem Insert holst du die letzte ID und gut ist. Wenn du die Verbindung trennst ist IMHO Schluss mit der letzten ID holen.



			
				Leroy42 hat gesagt.:
			
		

> Von mySQL oder von SQL selbst? Dann müßte ich ja sicher sein, daß auch ein mySQL-Server läuft



Also, da "auto_increment" auch MySQL-spezifisch ist bin ich davon ausgegangen, dass du MySQL nutzt. Das funktioniert so nur mit MySQL. Mit anderen DB-Systemen gibts andere Tricks um an die letzte ID zu kommen.


----------



## Leroy42 (20. Apr 2006)

Caffè Latte hat gesagt.:
			
		

> Leroy42 hat gesagt.:
> 
> 
> 
> ...


Das war nicht ganz meine Frage, aber ich stelle sie gleich in einem neuen Thread.[/quote]



			
				Caffè Latte hat gesagt.:
			
		

> Also, da "auto_increment" auch MySQL-spezifisch ist bin ich davon ausgegangen, dass du MySQL nutzt. Das funktioniert so nur mit MySQL. Mit anderen DB-Systemen gibts andere Tricks um an die letzte ID zu kommen.



Ich dachte zumindest daß dies eine häufig benötigte Funktionalität ist und diese somit auch in SQL definiert ist.
Aber egal, da mein Provider ja mySQL nutzt ist es eh egal.


----------

