pl: MYSQL Update wenn leer ansonsten insert

Beitrag lesen

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