MH: MYSQL Update wenn leer ansonsten insert

Moin,
ich hab mal wieder ein Problem...
Und zwar hab ich eine DB in der man über ein Formular Daten eintragen kann (was ein wunder ;)). Man muss allerdings nicht jedes Feld ausfüllen, sondern kann auch welche frei lassen. Die Daten sollen dann in die DB geschrieben werden. Nun will ich das so machen, dass falls in einer Zeile in der Spalte noch eine leere Zelle ist, der Eintrag dann da rein geschrieben wird. Sollte es keine leere Zelle mehr geben, soll eine neue Zeile erstellt werden.
Also quasi, wenn eine leere Zelle existiert dann UPDATE ansonsten INSERT INTO.

Ich hab das jetzt schon mal so ausprobiert, dass erst geprüft wird ob es eine leere Zelle gibt. Wenn ja wird die mit Update befüllt ansonsten wird ein INSERT INTO ausgeführt. Leider hat das nicht so geklappt wie es sollte, denn z.T. hat der einfach eine neue Zeile begonnen obwohl noch eine Zelle frei war.

Hat jmd eine Idee wie das funktionieren könnte, oder was an meine Code falsch ist?

// Das ist jetzt mein Test-Script. Am Ende wir das Array $_POST sein. $names= array( 'Matthias' => 'test', 'Judith' => 'test2', 'Markus' => 'test3', 'Annelene' => 'test4', 'Michael' => 'test5', 'Peter' => 'test6', 'Susanne' => 'test7', 'Janne' => 'test8', 'Lasse' => 'test9', 'Maya' => 'test10' ); // Für jeden Eintrag im Array wiederholen foreach ($names as $key => $value) { // Abfrage ob eine Zelle leer $query = sprintf( "SELECT * FROM `names` WHERE `%s` = ''", $mysqli->real_escape_string($key) ); $abf = $mysqli->query($query); $row = $abf->fetch_array(MYSQLI_ASSOC); // Wenn anzahl der zurückgegebene Zeilen größer 1 ist if ($abf->num_rows > 1) { // Tabelle Updaten und neuen Eintrag einsetzten wo Zelle leer $query2 = sprintf( "UPDATE `names` SET `%s` = '%s' WHERE `%s` = '' AND `id` = '%s'", $mysqli->real_escape_string($key), $mysqli->real_escape_string($value), $mysqli->real_escape_string($key), $mysqli->real_escape_string($row['id']) ); $mysqli->query($query2); } else { // Neue Zeile erstellen $query2 = sprintf( "INSERT INTO `names` (`%s`) VALUES ('%s')", $mysqli->real_escape_string($key), $mysqli->real_escape_string($value) ); $mysqli->query($query2); } }

Danke schon mal,
MH

  1. Hello,

    das klingt bezüglich des Datenmodells nicht sehr gesund.

    Wenn Du das aber trotzdem unbedingt so aufbauen willst, scjreib dir dafür eine stored Procedure. Dann musst Du der nur den Namen übergeben uns sie erledigt das für Dich.

    Wenn man die geschickt aufbaut, kann man auch die entstehende Racecondition zwischen Abfrage und Update abfangen, ohne die Tabelle (oder den Datensatz) sperren zu müssen.

    Liebe Grüße
    Tom S.

    -- Es gibt nichts Gutes, außer man tut es
    Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.

    Folgende Nachrichten verweisen auf diesen Beitrag:

    1. Moin,
      erstmal danke für die Antwort :)

      Das mit dem Stored Procedure schau ich mir mal an. Hab damit bis jetzt nich nicht gearbeitet.

      das klingt bezüglich des Datenmodells nicht sehr gesund.

      Hast du eine bessere Idee wie ich das aufbauen könnte?

      Gruß,
      MH

      1. Hello,

        Hast du eine bessere Idee wie ich das aufbauen könnte?

        Eine bessere nicht. Denn sie Geschäftsregeln der Datenhaltung sollten schon im DBMS verankert werden, damit man sie nicht umgehen kann.

        Eine andere schon:
        Du kannst die Abarbeitung auch in der API, also mittels PHP lösen.

        Pseudocode:

        Schleife bis Erfolg oder Ende Datensatz auslesen mit `select` case Feld1 leer updateversuch `where id=$id and feld1 == leer` affected Rows == 1 -> Erfolg break case Feld2 leer updateversuch `where id=$id and feld2 == leer` affected Rows == 1 -> Erfolg break ... case FeldX leer updateversuch `where id=$id and feldX == leer` affected Rows == 1 -> Erfolg -> Ende (break) — Schleifenende — wenn nicht Erfolg insert

        So wird immer nur ein Updateversuch auf das erste leere Feld vorgenommen. Wenn dieses aber zwischen dem Select und dem Versuch schon von einem anderen Prozess belegt wurde, wird die Schleife nochmals durchlaufen. Das geschieht im Zweifeldfall sooft, bis auch das letzte relevante Feld geprüft wurde. Scheitert das Update dort auch nochmal, wird ein Insert vorgenommen.

        Das schützt Dich aber nicht davor, dass Du nachher lauter Datensätze mit nur einem belegten Feld hast. Das müsstest Du noch extra abfangen.

        Besser wäre es daher, Du würdest normalisieren und dir eine Tabelle aufbauen, die pro Kopfdatensatz N Positionsdatensätze (die ehemaligen Felder) erhalten kann. Die Anzahl der Positionsdatensätze kannst Du mittels Subselect vorher abfragen.

        Liebe Grüße
        Tom S.

        -- Es gibt nichts Gutes, außer man tut es
        Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
      2. Hello,

        Hast du eine bessere Idee wie ich das aufbauen könnte?

        Noch eine andere (NICHT bessere!) Lösung.

        Schreib alle Namen in ein Feld, mit Komma getrennt. Dann darf im Namen selber kein Komma enthalten sein!

        Dann kannst Du mit find_in_set($neuerName, Spaltenname) prüfen, ob der Name schon drinsteht und ihn nur dann mittels Update hinzufügen, wenn das NICHT der fall ist.

        update table set spalte = TRIM(BOTH ',' FROM concat(spalte, ',', neuerName)) where id=$id and find_in_set($neuerName, Spalte) <> 0`

        Und dann wieder die affected_rows prüfen. Falls die !== 1 sind, das Insert durchführen.

        Das bleibt aber trotzdem in mehrfacher Hinsicht eine Schmuddellösung. Diese würde aber prüfen, ob der Name schon drinsteht. Sie würde aber (noch) nicht prüfen, wieviele Einträge das Set schon hat.

        Liebe Grüße
        Tom S.

        -- Es gibt nichts Gutes, außer man tut es
        Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
  2. Also quasi, wenn eine leere Zelle existiert dann UPDATE ansonsten INSERT INTO.

    Normalerweise macht man das ja genau umgekehrt, also insert ... on duplicate key update ...

    Da hat der Tom schon recht mit seiner Feststellung bezüglich Datenmodell. Ich würde mir das nochmal überlegen, insbesondere hinsichtlich Datenkonsistenz. MfG

    1. Hello,

      Also quasi, wenn eine leere Zelle existiert dann UPDATE ansonsten INSERT INTO.

      Normalerweise macht man das ja genau umgekehrt, also insert ... on duplicate key update ...

      Da hat der Tom schon recht mit seiner Feststellung bezüglich Datenmodell. Ich würde mir das nochmal überlegen, insbesondere hinsichtlich Datenkonsistenz. MfG

      Du hast da aber die Regel noch nicht richtig verstanden.

      Tabelle A mit Kopfdaten Tabelle B mit Positionsdaten.

      Eingetragen werden soll ein neuer Datensatz in B mit (A-id = A.id, Name), aber nur dann, wenn nicht schon N Zeilen mit A-id in B vorhanden sind.
      Sind schon N Zeilen vorhanden, muss erst in A eine neue Zeile erzeugt werden und dann erst in B die Zeile mit (A-id = A.(LAST_INSERT_ID), Name) eingetragen werden.

      Das ganze Problem reduduziert sich also auf ein oder zwei inserts mit einem Count auf B zwischendurch, alles verpaclt in einem Trigger.

      Wenn man in B (A-id, Name, posnr = count(B, ...)+1) einträgt, kann man allerdings mittels uniq-Key auf (A-id, posnr) auch die racecondition abfangen. Der Trigger muss im Konfliktfall dann ggf. eine Exception werfen. Ob man die rows-inserted innerhalb des Triggers ermitteln kann, weiß ich noch nicht.

      Ich wede das morgen früh am Desktop mal ausprobieren.

      Liebe Grüße
      Tom S.

      -- Es gibt nichts Gutes, außer man tut es
      Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
  3. Hallo MH,

    Abgesehen vom merkwürdigen Datenmodell würde ich sagen, dass du die Entscheidung INSERT Vs UPDATE durch einen Test > 0 treffen solltest, nicht > 1. Du willst updaten wenn mindestens ein freier Satz da ist, testest aber auf 2 freie Sätze.

    Für das Datenmodell solltest du eine Tabelle mit 3 Spalten nehmen: id, name, value. Und nicht eine Spalte pro Name.

    Eventuell eine Tabelle mit allen erlaubten Namen dazu, wenn beliebige Namen nicht erwünscht sind. Dann steht in der names Tabelle kein Klarname mehr, sondern eine NameId. Code und Queries sehen dann natürlich ganz anders aus.

    Rolf

    -- Dosen sind silbern
    1. Es gibt solche Datenmodelle lieber Rolf,

      die sind aber eher objektorientiert als relational. Wir habn hier einen abstrakten Datentyp, also Key => Value wenn wir mal die ID außer Acht lassen.

      Der gewünschten Operation entsrpicht die PHP Funktion array_merge() und in Perl würde man einfach %legacy = (%legacy, %input); notieren mit dem Ergebnis, dass neue Schlüssel-Werte-Paare eingetragen und Legacy-Werte überschrieben werden.

      Wenn man dafür sorgt, dass im assoziativen Array %input jeder Schlüssel einen Wert hat, ist die Aufgabe erfüllt. Eine solchen Datentyp kann man in einer Tabelle mit 2 Feldern speichern, sofern eine ID hinzukommt, sind das dann 3 Felder.

      Unten stelle ich mal ein bischen Perlcode an, das erlaubt sogar nichtlineare Hashes in einer 3spaltigen Tabelle. Im Grunde nur eine Alternative zur Datenhaltung in Datei: Jedesmal wenn die Daten gebraucht und ggf. bearbeitet werden sollen, muss der gesamte Hash/Array aus der Tabelle rausgeholt werdn und lümmelt sich dann im Hauptspeicher rum.

      package DBFreezeHash; use strict; use warnings; use DBI; use bytes; ########################################################################### sub new{ my $class = shift; my %cfg = ( user => '', pass => '', base => '', tabn => '', host => 'localhost', port => 3306, create => 0, @_); return eval{ my $dbh = DBI->connect_cached( "DBI:mysql:$cfg{base}:$cfg{host}:$cfg{port}", $cfg{user}, $cfg{pass}, {RaiseError => 1, PrintError => 0} ); my $self = bless{ lfdnr => 1, DBH => $dbh, CFG => \%cfg, }, $class; $self->{TABN} = $self->{DBH}->quote_identifier($cfg{tabn}); $self->_create if $cfg{create}; $self; }; } # Laufende Nummer zur Verwaltung der eigenen Entities sub _lfdnr{ my $self = shift; my $countup = shift || 0; return $countup ? ++$self->{lfdnr} : $self->{lfdnr}; } sub _create{ my $self = shift; my $q = qq( CREATE TABLE IF NOT EXISTS $self->{TABN}( `ent` varchar(128) COLLATE utf8_bin NOT NULL DEFAULT '', `att` varchar(128) COLLATE utf8_bin NOT NULL DEFAULT '', `val` text, PRIMARY KEY (`ent`,`att`), KEY ent(ent), KEY att(att) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ); $self->{DBH}->do($q); } sub _read{ my $self = shift; $self->{EAV} = {}; my %CHLD = (); # parent-children relation my $raw = $self->{DBH}->selectall_arrayref("SELECT * FROM $self->{TABN}", { Slice => {} }); foreach my $r( @$raw ){ my $def = $r->{def}; my $ent = $r->{ent}; my $att = $r->{att}; my $val = $r->{val}; $self->{EAV}{$ent}{$att} = $val; if( $att eq 'parent' ){ push @{$CHLD{$val}}, $ent; } } $self->{CHILDREN} = \%CHLD; } # wird rekursiv aufgerufen, aus der linearen EAV Struktur # das Original wiederherstellen sub _restore{ my $self = shift; my $href = shift; # aktueller stub my $hunt = shift; # hash der angefügt werden soll if( $hunt->{type} eq 'S' ){ $href->{$hunt->{att}} = $hunt->{val}; } else{ # hier der rekursive Aufruf $href->{$hunt->{att}} = {}; foreach my $child( @{$self->{CHILDREN}{$hunt->{ent}}} ){ $self->_restore($href->{$hunt->{att}}, $self->{EAV}{$child}); } } } sub freeze{ my $self = shift; my $ds = shift; my $parent = shift || $self->{lfdnr}; # looking for the insert statement if(! exists $self->{STH}{FREEZE}){ $self->{STH}{FREEZE} = $self->{DBH}->prepare(qq( INSERT INTO $self->{TABN}(ent, att, val) VALUES(?,?,?) )); } if( $self->{lfdnr} == 1 ){ $self->{DBH}->do("truncate $self->{TABN}"); } # Hash wird rekursiv durchlaufen und linearisiert # Jeder Eintrag bekommt eine fortlaufende Nummer foreach my $key( keys %$ds ){ if( ref $ds->{$key} eq 'HASH' ){ my $ent = $self->_lfdnr(1); $self->{STH}{FREEZE}->execute($ent,'type','H'); $self->{STH}{FREEZE}->execute($ent,'att',$key); $self->{STH}{FREEZE}->execute($ent,'parent',$parent); $self->{STH}{FREEZE}->execute($ent,'ent',$ent); $self->freeze( $ds->{$key} ); } else{ my $ent = $self->_lfdnr(1); my $def = defined $ds->{$key} ? 1 : 0; $self->{STH}{FREEZE}->execute($ent, 'parent',$parent); $self->{STH}{FREEZE}->execute($ent, 'type','S'); $self->{STH}{FREEZE}->execute($ent, 'att',$key); $self->{STH}{FREEZE}->execute($ent, 'val',$ds->{$key}); $self->{STH}{FREEZE}->execute($ent, 'ent',$ent); } } $self->{STH}{FREEZE}->finish; } # Datenstruktur wiederherstellen sub thaw{ my $self = shift; # aus Datei deserialisieren, Ergebnis ist # eine linearisierte Datenstruktur $self->_read; # die ursprüngliche Datenstruktur wiederherstellen $self->{RESTORED} = {}; # Knoten direkt unterhalb der Wurzel haben entity 1 foreach my $root( @{$self->{CHILDREN}{1}} ){ if( $self->{EAV}{$root}{type} eq 'S' ){ $self->{RESTORED}{$self->{EAV}{$root}{att}} = $self->{EAV}{$root}{val}; } else{ $self->{RESTORED}{$self->{EAV}{$root}{att}} = {}; foreach my $child ( @{$self->{CHILDREN}{$root}} ){ $self->_restore($self->{RESTORED}{$self->{EAV}{$root}{att}}, $self->{EAV}{$child}); } } } return $self->{RESTORED}; } 1;######################################################################### __END__ # Zum Testen Token entfernen und Datei ausführen use Data::Dumper; use strict; use warnings; my $ds = { #bin => join('', map{pack "C", $_}0..255), name => 'boo', nixdef => undef, void => '', addr => { addr_name => 'foo', addr_vname => 'bar' }, base => { base_addr => { base_addr_name => 'foo', base_addr_vname => 'bar', base_addr_undef => undef, base_addr_hash => { base_addr_hash_name => 'otto' }, }, }, #sig => \%SIG, #env => \%ENV, }; my $fr = DBFreezeHash->new( base => 'myweb', tabn => 'freezedhash', create => 1, ) or die $@; $fr->freeze( $ds ); my $r = $fr->thaw; print Dumper $r,$ds
      1. Hallo pl,

        Wir haben hier einen abstrakten Datentyp, also Key => Value (...) Der gewünschten Operation entspricht die PHP Funktion array_merge()…

        Äh - nein und nein. array_merge überschreibt bei mehrfachem Auftauchen eines Key, aber hier soll nicht überschrieben werden sondern angehängt.

        Ich lese die Aufgabenstellung so, dass es da einige Namen gibt, und wenn zu einem Namen ein Wert eingegeben wird, wird der gespeichert. Werden in nachfolgenden Formular-POSTs nochmal Werte zum Namen angegeben, werden diese Werte auch gespeichert. Zu jedem Namen N können also k_N Werte gespeichert sein, und MH möchte das so machen, dass die DB nicht mehr Zeilen enthält, als der Wert des größten k_N. Namen, die weniger Werte enthalten, haben Leerwerte in den entsprechenden Rows.

        Der abstrakte Datentyp ist also ein Dictionary, Key-Typ ist String und Wertetyp ist List<String>. Das von MH angedachte Mapping auf SQL ist suboptimal, weil es einen ganzen Haufen Anomalien gibt:

        • ein neuer Name verlangt eine DB-Modifikation (neue Spalte).
        • ein neuer Wert verlangt einen Table-Scan, um freien Platz zu finden
        • Ermitteln aller Werte zu einem Namen verlangt einen kompletten Table-Scan
        • Updates verlangen im Prinzip einen Table-Lock, weil sonst Race-Conditions bei der Freiplatzsuche entstehen können
        • Wenn Werte gelöscht werden, entstehen Lücken. Sollen die einfach stehen bleiben und später recycled werden? Kann man machen, aber wenn viel gelöscht wird, ist die Table nur noch sehr mager gefüllt. D.h. Löschen verlangt Reorganisation in den Datensätzen, das ist Handarbeit an der Datenhaltung. Und die vermeidet man, wenn ein anderes DB-Modell diese Handarbeit wegautomatisiert.

        Ein für relationale DBs brauchbareres DB-Layout habe ich bereits benannt.

        Deinen PERL Teil habe ich überflogen, aber nicht genauer studiert. Erstens kann ich kein Perl, zweitens ging es um PHP, drittens habe ich abgeschaltet als ich dein EAV-Konzept wiedererkannt habe. Hier werden keine Entities mit Attribut-Value Paaren gespeichert, sondern Namen mit Wertelisten. Insofern bin ich der Meinung, dass man deinen vorgeschlagenen Ansatz möglicherweise mit dem Holzhammer passend zum Problem machen kann. Aber ich programmiere lieber zärtlicher. Daher: so toll EAV in anderen Einsatzgebieten sein mag - hier eher nicht.

        Rolf

        -- Soßen sind braun
        1. Äh - nein und nein. array_merge überschreibt bei mehrfachem Auftauchen eines Key, aber hier soll nicht überschrieben werden sondern angehängt.

          Genau das macht der Perl Code %y = (%y, %x);

          und mit Sicherheit gibt es auch eine PHP Funktion dafür. Wenn auf ein Attribut mehrere Values kommen sollen ist das auch kein Problem, dann ist Value eben ein Array.

          Aber wie bereits festgestellt, solch Datentyp in einer DB abbilden zu wollen, ist nicht die primäre Frage. Primäre Frage vielmehr ist es, den passenden Datentyp zu finden. Die Frage nach dem Speicherort ist sekundär.

          MfG

  4. Hallo MH,

    falls in einer Zeile in der Spalte noch eine leere Zelle ist,
    Sollte es keine leere Zelle mehr geben, soll eine neue Zeile erstellt werden.

    Ich habe mich wirklich bemüht, das zu verstehen. Was sind denn in deinem Datenmodell Zeilen, Spalten und Zellen?

    Bis demnächst
    Matthias

    -- Rosen sind rot.
    1. Hello,

      Hallo MH,

      falls in einer Zeile in der Spalte noch eine leere Zelle ist,
      Sollte es keine leere Zelle mehr geben, soll eine neue Zeile erstellt werden.

      Ich habe mich wirklich bemüht, das zu verstehen. Was sind denn in deinem Datenmodell Zeilen, Spalten und Zellen?

      Ich stelle mir das so vor, wie eine Vorgangsbearbitung, wie z. B. für ein Spiel oder Rechnungen. Die würde man aber minimal mit zwei Tabellen aufbauen. Eine für die Kopfdaten (die Spielrunde, die Rechnungsnummer) und eine weitere für die Positionsdaten (Teilnehmer, Rechnungspositionen).

      Zusatzbedingung ist, dass pro Spielrunde nur zehn Teilnehmer (Positionen) zulässig sind. Das könnte man z. B. durch einen Trigger oder vermutlich auch durch ein Subquery abfangen.

      Zellen sind übrigens die Spaltenplätze innerhalb eines einzigen Datensatzes.

      Liebe Grüße
      Tom S.

      -- Es gibt nichts Gutes, außer man tut es
      Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
      1. Hallo,

        Zellen sind übrigens die Spaltenplätze innerhalb eines einzigen Datensatzes.

        Ich kann mich täuschen, aber ich traue dieses Wissen dem Matthias durchaus zu…

        Gruß
        Kalk

        1. Hello,

          Zellen sind übrigens die Spaltenplätze innerhalb eines einzigen Datensatzes.

          Ich kann mich täuschen, aber ich traue dieses Wissen dem Matthias durchaus zu…

          Und ich fand die Beschreibung von MH durchaus verständlich und begrifflich auch stimmig. Das unpraktische oder falsche Datenmodell steht auf einem anderen Blatt ;-)

          Liebe Grüße
          Tom S.

          -- Es gibt nichts Gutes, außer man tut es
          Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
      2. Hallo TS,

        falls in einer Zeile in der Spalte noch eine leere Zelle ist,
        Sollte es keine leere Zelle mehr geben, soll eine neue Zeile erstellt werden.

        Ich habe mich wirklich bemüht, das zu verstehen. Was sind denn in deinem Datenmodell Zeilen, Spalten und Zellen?

        Ich stelle mir das so vor, wie eine Vorgangsbearbitung, wie z. B. für ein Spiel oder Rechnungen.

        Ah ja. Ok, das könnte sein.

        Also:

        falls in einer Zeile in der Spalte noch eine leere Zelle ist,

        falls in einem Datensatz ein bestimmtes Attribut noch keinen Wert hat

        Sollte es keine leere Zelle mehr geben, soll eine neue Zeile erstellt werden.

        hat jedes Attribut des entsprechenden Datensatzes einen Wert, so soll ein neuer Datensatz erstellt werden,

        und jetzt kommt das Doofe:
        der sich dann vom urprünglichen Datensatz abgesehen von der ID an genau einer Stelle vom ursprünglichen unterscheidet.

        Bis demnächst
        Matthias

        -- Rosen sind rot.
        1. Hello,

          Sollte es keine leere Zelle mehr geben, soll eine neue Zeile erstellt werden.

          hat jedes Attribut des entsprechenden Datensatzes einen Wert, so soll ein neuer Datensatz erstellt werden,

          Ich würde bei DB-Tabellen hier nicht von Attributen reden. Attribute einer Zelle bzw. Spalte wären Spaltentyp, Bereich/Länge, Codierung, usw.

          und jetzt kommt das Doofe:
          der sich dann vom urprünglichen Datensatz abgesehen von der ID an genau einer Stelle vom ursprünglichen unterscheidet.

          Das verstehe ich jetzt wieder nicht.

          Liebe Grüße
          Tom S.

          -- Es gibt nichts Gutes, außer man tut es
          Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
          1. Hallo TS,

            Ich würde bei DB-Tabellen hier nicht von Attributen reden. Attribute einer Zelle bzw. Spalte wären Spaltentyp, Bereich/Länge, Codierung, usw.

            https://de.wikipedia.org/wiki/Relationale_Datenbank#Gegen.C3.BCberstellung_von_Grundbegriffen

            und jetzt kommt das Doofe:
            der sich dann vom urprünglichen Datensatz abgesehen von der ID an genau einer Stelle vom ursprünglichen unterscheidet.

            Das verstehe ich jetzt wieder nicht.

            Wenn man das (kaputte) Datenmodell aus dem OP voraussetzt, wäre das Einfügen eines neuen Datensatzes ein Duplikat des ersten bis auf den einen geänderten Wert.

            Bis demnächst
            Matthias

            -- Rosen sind rot.
            1. Hello,

              Hallo TS,

              Ich würde bei DB-Tabellen hier nicht von Attributen reden. Attribute einer Zelle bzw. Spalte wären Spaltentyp, Bereich/Länge, Codierung, usw.

              https://de.wikipedia.org/wiki/Relationale_Datenbank#Gegen.C3.BCberstellung_von_Grundbegriffen

              Ich beschrieb die Tabelle, Du die Beziehungen der Daten. Aber auch dann müsste es mMn heiße "wenn in eimem Tupel noch ein Attributwert frei ist", ode?

              und jetzt kommt das Doofe:
              der sich dann vom urprünglichen Datensatz abgesehen von der ID an genau einer Stelle vom ursprünglichen unterscheidet.

              Das verstehe ich jetzt wieder nicht.

              Wenn man das (kaputte) Datenmodell aus dem OP voraussetzt, wäre das Einfügen eines neuen Datensatzes ein Duplikat des ersten bis auf den einen geänderten Wertes

              Nein, ich denke, der müsste dann leer sein bis auf eine Zelle oder einen Attributwert des Tupels.

              Liebe Grüße
              Tom S.

              -- Es gibt nichts Gutes, außer man tut es
              Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
              1. Hallo TS,

                Ich würde bei DB-Tabellen hier nicht von Attributen reden. Attribute einer Zelle bzw. Spalte wären Spaltentyp, Bereich/Länge, Codierung, usw.

                https://de.wikipedia.org/wiki/Relationale_Datenbank#Gegen.C3.BCberstellung_von_Grundbegriffen

                Ich beschrieb die Tabelle, Du die Beziehungen der Daten. Aber auch dann müsste es mMn heiße "wenn in eimem Tupel noch ein Attributwert frei ist", ode?

                Das wäre auch richtig, ja (auch wenn es keiner, den ich kenne, so sagen würde), aber Matthias Verwendung von „Attribut“ war hier nicht falsch und die Verwendung von Attribut in diesem Zusammenhang ist durchaus gebräuchlich. Das, was du unter Attribut verstehst, bezeichnet man eher als die Metadaten oder das Schema einer Tabelle.

                LG,
                CK

                -- https://wwwtech.de/about
        2. Hallo Matthias,

          hier übersetzt du falsch (oder anders gesagt: bist um 90° daneben):

          Sollte es keine leere Zelle mehr geben, soll eine neue Zeile erstellt werden.

          hat jedes Attribut des entsprechenden Datensatzes einen Wert, so soll ein neuer Datensatz erstellt werden,

          D.h. du betrachtest das horizontal (innerhalb eines Satzes). Gemeint ist das nach meiner Deutung aber vertikal, also so:

          hat dieses Attribut in allen Datensätzen bereits einen Wert, so soll ein neuer Datensatz erstellt werden.

          Ich kann natürlich nicht ausschließen, dass ihr schon längst die ganze Betrachtung um 90° gedreht habt und ich das nicht bemerkt habe.

          Wie auch immer: ich habe MH so verstanden. Er überträgt im Prinzip eine Papierliste nach SQL. Die Papierliste hat einige Spalten (die Namen), und unter jeden Namen sollen beliebig viele Werte geschrieben werden können.

          Mal angenommen, Fridolin hat 3 Werte. Dann soll die DB 3 Zeilen haben. Solange kein anderer Name mehr als 3 Werte bekommt, bleibt es bei 3 Zeilen. Bekommt irgendein Name seinen vierten Wert, wird eine neue Zeile gebraucht und es ist ein INSERT fällig.

          KANN man so machen, ist aber ineffizient (Aufzählung der Anomalien in meinem Beitrag von heute 21:43 Uhr) und nicht idiomatisch für eine relationale DB.

          Korrekt wäre mMn eine Tabelle mit den Spaltenüberschriften (und einer Spalten-ID) - damit man im DB-Modell nicht auf eine bestimmte Anzahl von Namen festgelegt ist. Und dann eine Tabelle mit Spalten-ID und Wert, wo zu einer Spalten-ID beliebig viele Werte vorkommen können. Wenn eine Ordnung gebraucht wird, kann man noch eine laufende Nummer oder ein Zugangsdatum hinzufügen. Alles andere, was ich hier bisher gesehen habe, war weit weg von dem, was ich in Datenmodellierung gelernt habe. Oder ich hab's nicht kapiert - das ist immer die Default-Option 😉.

          Rolf

          -- Dosen sind silbern
          1. Hallo Rolf ẞ,

            Oder ich hab's nicht kapiert - das ist immer die Default-Option 😉.

            Wahrscheinlich trifft das auf mich zu.

            Bis demnächst
            Matthias

            -- Rosen sind rot.