Insert bei normalisierten Datenbanken

Hannes

Benutzer
Beiträge
12
Hallo in die Runde und alles Gute fürs neue Jahr,
ich versuche seit einiger Zeit der Frage auf den Grund zu gehen, wie ich Daten in eine normalisierte Datenbank bekomme.
Das Thema Normalisierung von Datenbanken scheint an allen Ecken und Enden thematisiert zu werden.
Die Frage bleibt dann für mich, wie jetzt mit dieser Datenbank arbeiten? Ich will ja nicht nur einen Festdatenbestand abfragen.

Natürlich habe ich eine Menge herumgesucht. Ansatzweise wird das Thema auch hier im Forum besprochen.

Weitere Beispiele wären hier zu finden:
Bsp01 und Bsp02
Der Weg über Slave-Tables scheint mir halbwegs einzuleuchten.

Bsp03
Hier verstehe ich das Konzept "insert ... on conflict" auch, denke ich.

Unterm Strich bekomme ich die Fäden aber nicht ganz zusammen und stehe irgendwie auf dem Schlauch.
Bzw. warum gibt es so wunderbar viele Erklärungen dazu, wie ich Datenbanken normalisiere, nicht aber wie ich damit arbeite (Abfragen ausgenommen)?

Kann mir jemand die Logik näher bringen?
(in der Regel arbeite ich mit Postgresql)
 
Werbung:
Ich denke, Deine Hauptfrage ist die Arbeit mit Foreign Keys. Wenn Du z.B. 2 Tabellen hast, eine Master-Tabelle mit einem Primary Key, und eine Detailtabelle mit eine Foreign Key auf die Mastertabelle, und Du willst z.B. einen neuen Datensatz IN BEIDEN TABELLEN anlegen, und auch so, daß es atomar ist:

Code:
edb=# create table master(id int generated always as identity primary key, master_name text);
CREATE TABLE
edb=*# create table detail(id int generated always as identity primary key, m_id int references master, detail text);
CREATE TABLE
edb=*# with id as (insert into master(master_name) values ('test master name') returning id) insert into detail (m_id, detail) select id, 'das ist das Detail' from id; 
INSERT 0 1
edb=*# select * from master;
 id |   master_name    
----+------------------
  1 | test master name
(1 row)

edb=*# select * from detail;
 id | m_id |       detail       
----+------+--------------------
  1 |    1 | das ist das Detail
(1 row)

edb=*#

Wie zu sehen, ich habe AN KEINER STELLE die id's irgendwie im Wert genannt, das Insert hat atomar das Insert in master durchgeführt, die erhaltene id in einer virtuellen Zwischentabelle geliefert und diese diente dann als Quelle für das Insert in der Detailtabelle.

Verstanden soweit?
 
Hallo akretschmer,
das war ja eine schnelle Antwort! Danke!
Ich verdaue es gerade und verinnerliche noch das Konzept der CTE. Ich denke, ich verstehe es soweit. Ich probiere aber noch ein bischen herum.
 
Das muss man nicht unbedingt mit CTEs machen. Man kann auch den zuletzt generierten Wert einer IDENTITY Spalte mittels einer Funktion abfragen und dies in einem weiteren INSERT verwenden.

Code:
create table master
(
  id int generated always as identity primary key,
  master_name text
);

create table detail
(
  id int generated always as identity primary key,
  m_id int references master,
  detail text
);

insert into master (master_name)
values ('test master name');

insert into detail (m_id, detail)
values (currval('master_id_seq'), 'das ist das Detail');

Die Funktion currval() ist Postgres spezifisch. Andere Datenbanksysteme verwenden andere Funktionen. In Oracle wäre es z.B. master_id_seq.currval, SQL Server verwendet - glaube ich - @@identity
 
Die Funktion currval() ist Postgres spezifisch. Andere Datenbanksysteme verwenden andere Funktionen.
Vielleicht noch als Ergänzung.
Eine Sequence ist an der Stelle ein ziemlich verbreitetes, universelles Objekt bei vielen Herstellern, falls es um die Handhabung von ID geht. Aber das ist dann nur spannend, wenn es möglichst kompatibler Code sein soll. Die Sequenz Werte werden dann idR über Trigger bei Insert eingetragen.
Sequenzen bieten vergleichsweise viel Flexibilität und wie gesagt Kompatibilität, das Beispiel von @castorp weniger, ist dafür sicherer.
 
Die Sequenz Werte werden dann idR über Trigger bei Insert eingetragen.
Eigentlich nicht. In den meisten Fällen wird das eben über das GENERATED ALWAYS AS IDENTITY erreicht.

Sequenzen bieten vergleichsweise viel Flexibilität und wie gesagt Kompatibilität, das Beispiel von @castorp weniger, ist dafür sicherer.
Mein Beispiel verwendet auch eine Sequenz - so werden IDENTITY Spalten in Postgres (und anderen Datenbanksystemen) realisiert.
 
Ich wollte nur sagen, dass man allgemein (in verschiedenen DB) mit Sequenzen und Triggern die meisten Freiheitsgrade hat und (vermutlich) am meisten Kompatibilität. Wobei das beim Trigger Code auch schon wieder fragwürdig ist.
Im Trigger kann ich jedenfalls unter Verwendung einer Sequenz selbst entscheiden, was geschieht, wenn das Insert bereits mit einer ID rein kommt. Damit kann ich also ID wahlweise selbst bestimmen. Dabei muss man natürlich wissen, wozu und was zu beachten ist.
Vielleicht ein Randthema und für Anfänger nicht hilfreich.
 
Im Trigger kann ich jedenfalls unter Verwendung einer Sequenz selbst entscheiden, was geschieht, wenn das Insert bereits mit einer ID rein kommt.

In PG geht das einfacher: verwende eine Sequence, dann kannst Du auch selber die ID definieren, mit allen Konsequenzen. Oder verwende diese generated always as identity, dann ist es Dir expliziet verboten. Da extra Komplexität, Fehlerquellen und Performancebremsen einzubauen halte ich für nicht sinnvoll.
 
Werbung:
Zurück
Oben