From c9e341509dc67d0301a80d3645231312fdd034f6 Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Fri, 9 Nov 2018 14:13:44 +0000 Subject: [PATCH] 93_DbRep: contrib 8.9.5 git-svn-id: https://svn.fhem.de/fhem/trunk@17713 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/DS_Starter/93_DbRep.pm | 414 +++++++++++++++++++++++----- 1 file changed, 348 insertions(+), 66 deletions(-) diff --git a/fhem/contrib/DS_Starter/93_DbRep.pm b/fhem/contrib/DS_Starter/93_DbRep.pm index 2b22a9494..1428d756c 100644 --- a/fhem/contrib/DS_Starter/93_DbRep.pm +++ b/fhem/contrib/DS_Starter/93_DbRep.pm @@ -57,6 +57,8 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch'; # Versions History intern our %DbRep_vNotesIntern = ( + "8.9.5" => "09.11.2018 hash %dbrep_col substituted by get data from dblog device in func DbRep_firstconnect, fix importFromFile contains only SPACE ", + "8.9.0" => "07.11.2018 command delDoublets added ", "8.8.0" => "06.11.2018 first connect routine switched to DbRep_Main, get COLSET from DBLOG-instance, attribute 'fastStart' added ", "8.7.0" => "04.11.2018 attribute valueFilter applied to functions based on 'SELECT', 'UPDATE', 'DELETE' and 'valueFilter' generally applied to field 'VALUE' ", "8.6.0" => "29.10.2018 reduceLog use attributes device/reading (can be overwritten by set-options) ", @@ -123,6 +125,7 @@ our %DbRep_vNotesIntern = ( # Versions History extern: our %DbRep_vNotesExtern = ( + "8.9.0" => "07.11.2018 new command set delDoublets added. This command allows to delete multiple occuring identical records. ", "8.8.0" => "06.11.2018 new attribute 'fastStart'. Usually every DbRep-device is making a short connect to its database when " ."FHEM is restarted. When this attribute is set, the initial connect is done when the DbRep-device is doing its " ."first task. ", @@ -262,14 +265,6 @@ our %DbRep_vHintsExt_de = ( sub DbRep_Main($$;$); sub DbLog_cutCol($$$$$$$); # DbLog-Funktion nutzen um Daten auf maximale Länge beschneiden - -my %dbrep_col = ("DEVICE" => 64, - "TYPE" => 64, - "EVENT" => 512, - "READING" => 64, - "VALUE" => 128, - "UNIT" => 32 - ); ################################################################################### # DbRep_Initialize @@ -441,6 +436,7 @@ sub DbRep_Set($@) { (($hash->{ROLE} ne "Agent")?"sumValue:display,writeToDB ":""). (($hash->{ROLE} ne "Agent")?"averageValue:display,writeToDB ":""). (($hash->{ROLE} ne "Agent")?"changeValue ":""). + (($hash->{ROLE} ne "Agent")?"delDoublets:adviceDelete,delete ":""). (($hash->{ROLE} ne "Agent")?"delEntries:noArg ":""). (($hash->{ROLE} ne "Agent")?"delSeqDoublets:adviceRemain,adviceDelete,delete ":""). "deviceRename ". @@ -549,12 +545,12 @@ sub DbRep_Set($@) { return undef; } - if ($opt =~ m/delSeqDoublets/ && $hash->{ROLE} ne "Agent") { + if ($opt =~ m/delSeqDoublets|delDoublets/ && $hash->{ROLE} ne "Agent") { $hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; if ($prop =~ /delete/ && !AttrVal($hash->{NAME}, "allowDeletion", 0)) { return " Set attribute 'allowDeletion' if you want to allow deletion of any database entries. Use it with care !"; } - DbRep_beforeproc($hash, "delSeq"); + DbRep_beforeproc($hash, "delDoublets"); DbRep_Main($hash,$opt,$prop); return undef; } @@ -717,12 +713,12 @@ sub DbRep_Set($@) { my $i_reading = AttrVal($hash->{NAME}, "reading", ""); # Daten auf maximale Länge (entsprechend der Feldlänge in DbLog DB create-scripts) beschneiden wenn nicht SQLite - if ($dbmodel ne 'SQLITE') { - $i_device = substr($i_device,0, $dbrep_col{DEVICE}); - $i_reading = substr($i_reading,0, $dbrep_col{READING}); - $i_value = substr($i_value,0, $dbrep_col{VALUE}); - $i_unit = substr($i_unit,0, $dbrep_col{UNIT}) if($i_unit); - } + #if ($dbmodel ne 'SQLITE') { + # $i_device = substr($i_device,0, $hash->{HELPER}{DBREPCOL}{DEVICE}); + # $i_reading = substr($i_reading,0, $hash->{HELPER}{DBREPCOL}{READING}); + # $i_value = substr($i_value,0, $hash->{HELPER}{DBREPCOL}{VALUE}); + # $i_unit = substr($i_unit,0, $hash->{HELPER}{DBREPCOL}{UNIT}) if($i_unit); + #} $hash->{HELPER}{I_TIMESTAMP} = $i_timestamp; $hash->{HELPER}{I_DEVICE} = $i_device; @@ -1221,17 +1217,12 @@ sub DbRep_Attr($$$$) { delete($attr{$name}{ftpUse}); } if ($aName eq "reading" || $aName eq "device") { - if ($dbmodel && $dbmodel ne 'SQLITE') { - my $attrname = uc($aName); - if ($dbmodel eq 'POSTGRESQL' && $aVal !~ m/,/) { - return "Length of \"$aName\" is too big. Maximum length for database type $dbmodel is $dbrep_col{$attrname}" if(length($aVal) > $dbrep_col{$attrname}); - } elsif ($dbmodel eq 'MYSQL' && $aVal !~ m/,/) { - return "Length of \"$aName\" is too big. Maximum length for database type $dbmodel is $dbrep_col{$attrname}" if(length($aVal) > $dbrep_col{$attrname}); - } + if ($aVal !~ m/,/ && $dbmodel && $dbmodel ne 'SQLITE') { + my $mlen = 64; + return "Length of \"$aName\" is too big. Maximum length for database type $dbmodel is $mlen" if(length($aVal) > $mlen); } - } - - } + } + } return undef; } @@ -1351,11 +1342,12 @@ return undef; sub DbRep_firstconnect(@) { my ($string) = @_; my ($name,$opt,$prop,$fret) = split("\\|", $string); - my $hash = $defs{$name}; - my $to = "120"; - my $dbloghash = $hash->{dbloghash}; - my $dbconn = $dbloghash->{dbconn}; - my $dbuser = $dbloghash->{dbuser}; + my $hash = $defs{$name}; + my $to = "120"; + $hash->{dbloghash} = $defs{$hash->{HELPER}{DBLOGDEVICE}}; + my $dbloghash = $hash->{dbloghash}; + my $dbconn = $dbloghash->{dbconn}; + my $dbuser = $dbloghash->{dbuser}; RemoveInternalTimer($hash, "DbRep_firstconnect"); return if(IsDisabled($name)); @@ -1366,13 +1358,13 @@ sub DbRep_firstconnect(@) { return; } # DB Struktur aus DbLog Instanz übernehmen - $hash->{HELPER}{DBREPCOL}{COLSET} = $dbloghash->{HELPER}{COLSET}; - $hash->{HELPER}{DBREPCOL}{DEVICECOL} = $dbloghash->{HELPER}{DEVICECOL}; - $hash->{HELPER}{DBREPCOL}{EVENTCOL} = $dbloghash->{HELPER}{EVENTCOL}; - $hash->{HELPER}{DBREPCOL}{READINGCOL} = $dbloghash->{HELPER}{READINGCOL}; - $hash->{HELPER}{DBREPCOL}{TYPECOL} = $dbloghash->{HELPER}{TYPECOL}; - $hash->{HELPER}{DBREPCOL}{UNITCOL} = $dbloghash->{HELPER}{UNITCOL}; - $hash->{HELPER}{DBREPCOL}{VALUECOL} = $dbloghash->{HELPER}{VALUECOL}; + $hash->{HELPER}{DBREPCOL}{COLSET} = $dbloghash->{HELPER}{COLSET}; + $hash->{HELPER}{DBREPCOL}{DEVICE} = $dbloghash->{HELPER}{DEVICECOL}; + $hash->{HELPER}{DBREPCOL}{TYPE} = $dbloghash->{HELPER}{TYPECOL}; + $hash->{HELPER}{DBREPCOL}{EVENT} = $dbloghash->{HELPER}{EVENTCOL}; + $hash->{HELPER}{DBREPCOL}{READING} = $dbloghash->{HELPER}{READINGCOL}; + $hash->{HELPER}{DBREPCOL}{VALUE} = $dbloghash->{HELPER}{VALUECOL}; + $hash->{HELPER}{DBREPCOL}{UNIT} = $dbloghash->{HELPER}{UNITCOL}; # DB Strukturelemente abrufen Log3 ($name, 3, "DbRep $name - Connectiontest to database $dbconn with user $dbuser") if($hash->{LASTCMD} ne "minTimestamp"); @@ -1502,12 +1494,14 @@ return; ################################################################################################################ sub DbRep_Main($$;$) { my ($hash,$opt,$prop) = @_; - my $name = $hash->{NAME}; - my $to = AttrVal($name, "timeout", "86400"); - my $reading = AttrVal($name, "reading", "%"); - my $device = AttrVal($name, "device", "%"); - my $dbloghash = $hash->{dbloghash}; - my $dbmodel = $dbloghash->{MODEL}; + my $name = $hash->{NAME}; + my $to = AttrVal($name, "timeout", "86400"); + my $reading = AttrVal($name, "reading", "%"); + my $device = AttrVal($name, "device", "%"); + my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE}; + $hash->{dbloghash} = $defs{$dblogdevice}; + my $dbloghash = $hash->{dbloghash}; + my $dbmodel = $dbloghash->{MODEL}; # Entkommentieren für Testroutine im Vordergrund # testexit($hash); @@ -1638,6 +1632,11 @@ sub DbRep_Main($$;$) { my $table = $prop; $hash->{HELPER}{RUNNING_PID} = BlockingCall("fetchrows_DoParse", "$name|$table|$device|$reading|$runtime_string_first|$runtime_string_next", "fetchrows_ParseDone", $to, "DbRep_ParseAborted", $hash); + } elsif ($opt =~ /delDoublets/) { + my $cmd = $prop?$prop:"adviceDelete"; + # delseqdoubl_ParseDone ist Auswertefunktion auch für delDoublets + $hash->{HELPER}{RUNNING_PID} = BlockingCall("deldoublets_DoParse", "$name§$cmd§$device§$reading§$ts", "delseqdoubl_ParseDone", $to, "DbRep_ParseAborted", $hash); + } elsif ($opt =~ /delSeqDoublets/) { my $cmd = $prop?$prop:"adviceRemain"; $hash->{HELPER}{RUNNING_PID} = BlockingCall("delseqdoubl_DoParse", "$name§$cmd§$device§$reading§$ts", "delseqdoubl_ParseDone", $to, "DbRep_ParseAborted", $hash); @@ -1673,6 +1672,13 @@ sub DbRep_Main($$;$) { $hash->{HELPER}{RUNNING_PID} = BlockingCall("diffval_DoParse", "$name§$device§$reading§$prop§$ts", "diffval_ParseDone", $to, "DbRep_ParseAborted", $hash); } elsif ($opt eq "insert") { + # Daten auf maximale Länge (entsprechend der Feldlänge in DbLog) beschneiden wenn nicht SQLite + if ($dbmodel ne 'SQLITE') { + $hash->{HELPER}{I_DEVICE} = substr($hash->{HELPER}{I_DEVICE},0, $hash->{HELPER}{DBREPCOL}{DEVICE}); + $hash->{HELPER}{I_READING} = substr($hash->{HELPER}{I_READING},0, $hash->{HELPER}{DBREPCOL}{READING}); + $hash->{HELPER}{I_VALUE} = substr($hash->{HELPER}{I_VALUE},0, $hash->{HELPER}{DBREPCOL}{VALUE}); + $hash->{HELPER}{I_UNIT} = substr($hash->{HELPER}{I_UNIT},0, $hash->{HELPER}{DBREPCOL}{UNIT}) if($hash->{HELPER}{I_UNIT}); + } $hash->{HELPER}{RUNNING_PID} = BlockingCall("insert_Push", "$name", "insert_Done", $to, "DbRep_ParseAborted", $hash); } elsif ($opt =~ /deviceRename|readingRename/) { @@ -4696,6 +4702,7 @@ sub fetchrows_ParseDone($) { my $zs = ""; # Zusatz wenn device + Reading + Timestamp von folgenden DS gleich ist UND Value unterschiedlich my $zsz = 1; # Zusatzzähler foreach my $row (@row_array) { + chomp $row; my @a = split("_ESC_", $row, 6); my $dev = $a[0]; my $rea = $a[1]; @@ -4758,7 +4765,158 @@ return; } #################################################################################################### -# DB-Abfrage delSeqDoublets +# Doubletten finden und löschen +#################################################################################################### +sub deldoublets_DoParse($) { + my ($string) = @_; + my ($name,$opt,$device,$reading,$ts) = split("\\§", $string); + my $hash = $defs{$name}; + my $dbloghash = $hash->{dbloghash}; + my $dbconn = $dbloghash->{dbconn}; + my $dbuser = $dbloghash->{dbuser}; + my $dblogname = $dbloghash->{NAME}; + my $dbpassword = $attr{"sec$dblogname"}{secret}; + my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0; + my $limit = AttrVal($name, "limit", 1000); + my ($err,$dbh,$sth,$sql,$rowlist,$selspec,$st,$table,$addon); + + # Background-Startzeit + my $bst = [gettimeofday]; + + eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 });}; + if ($@) { + $err = encode_base64($@,""); + Log3 ($name, 2, "DbRep $name - $@"); + return "$name|''|''|$err|''|$opt"; + } + + # Timestampstring to Array + my @ts = split("\\|", $ts); + Log3 ($name, 5, "DbRep $name - Timestamp-Array: \n@ts"); + + # mehrfache Datensätze finden + # SELECT TIMESTAMP, DEVICE, READING, VALUE, count(*) FROM history WHERE TIMESTAMP > '2018-11-01 00:00:00' GROUP BY TIMESTAMP, DEVICE, READING, VALUE ASC HAVING count(*) > 1 + $table = "history"; + $selspec = "TIMESTAMP,DEVICE,READING,VALUE,count(*)"; + $addon = "GROUP BY TIMESTAMP, DEVICE, READING, VALUE ASC HAVING count(*) > 1"; + + # SQL zusammenstellen für DB-Abfrage + $sql = DbRep_createSelectSql($hash,$table,$selspec,$device,$reading,"?","?",$addon); + $sth = $dbh->prepare_cached($sql); + + # DB-Abfrage zeilenweise für jeden Timearray-Eintrag + my @todel; + my $ntodel = 0; + my $ndel = 0; + my $rt = 0; + + no warnings 'uninitialized'; + + foreach my $row (@ts) { + my @a = split("#", $row); + my $runtime_string = $a[0]; + my $runtime_string_first = $a[1]; + my $runtime_string_next = $a[2]; + $runtime_string = encode_base64($runtime_string,""); + + # SQL-Startzeit + $st = [gettimeofday]; + + # SQL zusammenstellen für Logausgabe + my $sql1 = DbRep_createSelectSql($hash,$table,$selspec,$device,$reading,"'$runtime_string_first'","'$runtime_string_next'",$addon); + Log3 ($name, 4, "DbRep $name - SQL execute: $sql1"); + + eval{$sth->execute($runtime_string_first, $runtime_string_next);}; + if ($@) { + $err = encode_base64($@,""); + Log3 ($name, 2, "DbRep $name - $@"); + $dbh->disconnect; + return "$name|''|''|$err|''|$opt"; + } + + # SQL-Laufzeit ermitteln + $rt = $rt+tv_interval($st); + + # Beginn Löschlogik, Zusammenstellen der löschenden DS (warping) + # Array @warp -> die zu löschenden Datensätze + my (@warp); + my $i = 0; + foreach my $nr (map { $_->[1]."_ESC_".$_->[2]."_ESC_".($_->[0] =~ s/ /_ESC_/r)."_ESC_".$_->[3]."_|_".($_->[4]-1) } @{$sth->fetchall_arrayref()}) { + # Reihenfolge geändert in: DEVICE,READING,DATE,TIME,VALUE,count(*) + if($opt =~ /adviceDelete/) { + push(@warp,$nr) if($#todel+1 < $limit); # die zu löschenden Datensätze (nur zur Anzeige) + } else { + push (@warp,$nr); # Array der zu löschenden Datensätze + } + my $c = (split("|",$nr))[-1]; + $ntodel = $ntodel + $c; + + if ($opt =~ /delete/) { # delete Datensätze + my ($dev,$read,$date,$time,$val,$limit) = split(/_ESC_|_\|_/, $nr); + my $dt = $date." ".$time; + chomp($val); + $dev =~ s/'/''/g; # escape ' with '' + $read =~ s/'/''/g; # escape ' with '' + $val =~ s/'/''/g; # escape ' with '' + $st = [gettimeofday]; + my $dsql = "delete FROM $table WHERE TIMESTAMP = '$dt' AND DEVICE = '$dev' AND READING = '$read' AND VALUE = '$val' limit $limit;"; + my $sthd = $dbh->prepare($dsql); + Log3 ($name, 4, "DbRep $name - SQL execute: $dsql"); + + eval {$sthd->execute();}; + if ($@) { + $err = encode_base64($@,""); + Log3 ($name, 2, "DbRep $name - $@"); + $dbh->disconnect; + return "$name|''|''|$err|''|$opt"; + } + $ndel = $ndel+$sthd->rows; + $dbh->commit() if(!$dbh->{AutoCommit}); + + $rt = $rt+tv_interval($st); + } + + $i++; + } + + if(@warp && $opt =~ /adviceDelete/) { + push(@todel,@warp); + } + } + + Log3 ($name, 3, "DbRep $name - number records identified to delete by \"$hash->{LASTCMD}\": $ntodel") if($ntodel && $opt =~ /advice/); + Log3 ($name, 3, "DbRep $name - rows deleted by \"$hash->{LASTCMD}\": $ndel") if($ndel); + + my $retn = ($opt =~ /adviceDelete/)?$ntodel:$ndel; + my @retarray = ($opt =~ /adviceDelete/)?@todel:" "; + + s/\|/_E#S#C_/g for @retarray; # escape Pipe "|" + if ($utf8 && @retarray) { + $rowlist = Encode::encode_utf8(join('|', @retarray)); + } elsif(@retarray) { + $rowlist = join('|', @retarray); + } else { + $rowlist = 0; + } + + use warnings; + Log3 ($name, 5, "DbRep $name -> row result list:\n$rowlist"); + + $dbh->disconnect; + + # Daten müssen als Einzeiler zurückgegeben werden + $rowlist = encode_base64($rowlist,""); + + # Background-Laufzeit ermitteln + my $brt = tv_interval($bst); + + $rt = $rt.",".$brt; + +return "$name|$rowlist|$rt|0|$retn|$opt"; +} + +#################################################################################################### +# sequentielle Doubletten löschen #################################################################################################### sub delseqdoubl_DoParse($) { my ($string) = @_; @@ -4934,7 +5092,7 @@ return "$name|$rowlist|$rt|0|$retn|$opt"; } #################################################################################################### -# Auswertungsroutine delSeqDoublets +# Auswertungsroutine delSeqDoublets / delDoublets #################################################################################################### sub delseqdoubl_ParseDone($) { my ($string) = @_; @@ -4962,7 +5120,7 @@ sub delseqdoubl_ParseDone($) { } # Befehl nach Procedure ausführen - $erread = DbRep_afterproc($hash, "delSeq"); + $erread = DbRep_afterproc($hash, "delDoublets"); # Readingaufbereitung readingsBeginUpdate($hash); @@ -5241,14 +5399,13 @@ sub impfile_Push($) { foreach(@alarr) { tr/"//d; } - my $i_timestamp = $alarr[0]; - # $i_timestamp =~ tr/"//d; - my $i_device = $alarr[1]; - my $i_type = $alarr[2]; - my $i_event = $alarr[3]; - my $i_reading = $alarr[4]; - my $i_value = $alarr[5]; - my $i_unit = $alarr[6] ? $alarr[6]: " "; + my $i_timestamp = DbRep_trim($alarr[0]); + my $i_device = DbRep_trim($alarr[1]); + my $i_type = DbRep_trim($alarr[2]); + my $i_event = DbRep_trim($alarr[3]); + my $i_reading = DbRep_trim($alarr[4]); + my $i_value = DbRep_trim($alarr[5]); + my $i_unit = DbRep_trim($alarr[6] ? $alarr[6]: ""); $irowcount++; next if(!$i_timestamp); #leerer Datensatz @@ -5264,11 +5421,11 @@ sub impfile_Push($) { # Daten auf maximale Länge (entsprechend der Feldlänge in DbLog DB create-scripts) beschneiden wenn nicht SQLite if ($dbmodel ne 'SQLITE') { - $i_device = substr($i_device,0, $dbrep_col{DEVICE}); - $i_event = substr($i_event,0, $dbrep_col{EVENT}); - $i_reading = substr($i_reading,0, $dbrep_col{READING}); - $i_value = substr($i_value,0, $dbrep_col{VALUE}); - $i_unit = substr($i_unit,0, $dbrep_col{UNIT}) if($i_unit); + $i_device = substr($i_device,0, $hash->{HELPER}{DBREPCOL}{DEVICE}); + $i_event = substr($i_event,0, $hash->{HELPER}{DBREPCOL}{EVENT}); + $i_reading = substr($i_reading,0, $hash->{HELPER}{DBREPCOL}{READING}); + $i_value = substr($i_value,0, $hash->{HELPER}{DBREPCOL}{VALUE}); + $i_unit = substr($i_unit,0, $hash->{HELPER}{DBREPCOL}{UNIT}) if($i_unit); } Log3 ($name, 5, "DbRep $name -> data to insert Timestamp: $i_timestamp, Device: $i_device, Type: $i_type, Event: $i_event, Reading: $i_reading, Value: $i_value, Unit: $i_unit"); @@ -8390,7 +8547,7 @@ sub DbRep_checktimeaggr ($) { if ($aggregation ne "no") { $IsAggrSet = 1; } - if($hash->{LASTCMD} =~ /delSeqDoublets/) { + if($hash->{LASTCMD} =~ /delSeqDoublets|delDoublets/) { $aggregation = ($aggregation eq "no")?"day":$aggregation; # wenn Aggregation "no", für delSeqDoublets immer "day" setzen $IsAggrSet = 1; } @@ -9999,6 +10156,7 @@ return;
  • Repair of a corrupted SQLite database ("database disk image is malformed")
  • transmission of datasets from source database into another (Standby) database (syncStandby)
  • reduce the amount of datasets in database (reduceLog)
  • +
  • delete of duplicate records (delDoublets)

  • @@ -10227,7 +10385,67 @@ return;
    - +
  • delDoublets [adviceDelete | delete] - show respectively delete duplicate/multiple datasets. + Therefore the fields TIMESTAMP, DEVICE, READING and VALUE of records are compared.
    + The attributes to define the scope of aggregation,time period, device and reading are + considered. If attribute aggregation is not set or set to "no", it will change to the default aggregation + period "day". +

    + + +
    + + Due to security reasons the attribute attribute "allowDeletion" needs + to be set for execute the "delete" option.
    + The amount of datasets to show by commands "delDoublets adviceDelete" is initially limited + (default: 1000) and can be adjusted by attribute "limit". + The adjustment of "limit" has no impact to the "delDoublets delete" function, but affects + ONLY the display of the data.
    + Before and after this "delDoublets" it is possible to execute a FHEM command or Perl script + (please see attributes "executeBeforeProc", "executeAfterProc"). +

    + + +
    + + Zusammengefasst sind die zur Steuerung dieser Funktion relevanten Attribute:

    + + +
    +
    + +
  • +
  • delEntries - deletes all database entries or only the database entries specified by attributes Device and/or Reading and the entered time period between "timestamp_begin", "timestamp_end" (if set) or "timeDiffToNow/timeOlderThan".

    @@ -10338,8 +10556,8 @@ return; limit : limits ONLY the count of datasets to display device : include or exclude <device> from selection reading : include or exclude <reading> from selection - executeBeforeProc : execute a FHEM command (or perl-routine) before start of delEntries - executeAfterProc : execute a FHEM command (or perl-routine) after delEntries is finished + executeBeforeProc : execute a FHEM command (or perl-routine) before start of the function + executeAfterProc : execute a FHEM command (or perl-routine) after the function is finished seqDoubletsVariance : Up to this value consecutive numerical datasets are handled as identical and should be deleted time.* : a number of attributes to limit selection by time valueFilter : an additional REGEXP to control the record selection. The REGEXP is applied to the database field 'VALUE'. @@ -12198,6 +12416,7 @@ sub bdump {
  • Reparatur einer korrupten SQLite Datenbank ("database disk image is malformed")
  • Übertragung von Datensätzen aus der Quelldatenbank in eine andere (Standby) Datenbank (syncStandby)
  • Reduktion der Anzahl von Datensätzen in der Datenbank (reduceLog)
  • +
  • Löschen von doppelten Datensätzen (delDoublets)

  • @@ -12263,7 +12482,7 @@ sub bdump { IO::Uncompress::Gunzip
    Blocking (FHEM-Modul)

    - Aus Performancegründen sollten zusätzlich folgender Index erstellt werden:
    + Aus Performancegründen sollte zusätzlich folgender Index erstellt werden:
    CREATE INDEX Report_Idx ON `history` (TIMESTAMP, READING) USING BTREE; @@ -12429,6 +12648,67 @@ sub bdump {

    + +
  • delDoublets [adviceDelete | delete] - zeigt bzw. löscht doppelte / mehrfach vorkommende Datensätze. + Dazu wird Timestamp, Device,Reading und Value ausgewertet.
    + Die Attribute zur Aggregation,Zeit-,Device- und Reading-Abgrenzung werden dabei + berücksichtigt. Ist das Attribut "aggregation" nicht oder auf "no" gesetzt, wird im Standard die Aggregation + "day" verwendet. +

    + + +
    + + Aus Sicherheitsgründen muss das Attribut "allowDeletion" für die "delete" Option + gesetzt sein.
    + Die Anzahl der anzuzeigenden Datensätze des Kommandos "delDoublets adviceDelete" ist zunächst + begrenzt (default 1000) und kann durch das Attribut "limit" angepasst + werden. + Die Einstellung von "limit" hat keinen Einfluss auf die "delDoublets delete" Funktion, sondern + beeinflusst NUR die Anzeige der Daten.
    + Vor und nach der Ausführung von "delDoublets" kann ein FHEM-Kommando bzw. Perl-Routine ausgeführt + werden. (siehe Attribute "executeBeforeProc", "executeAfterProc") +

    + + +
    + + Zusammengefasst sind die zur Steuerung dieser Funktion relevanten Attribute:

    + + +
    +
    + +
  • delEntries - löscht alle oder die durch die Attribute device und/oder reading definierten Datenbankeinträge. Die Eingrenzung über Timestamps erfolgt @@ -12541,10 +12821,13 @@ sub bdump { allowDeletion : needs to be set to execute the delete option aggregation : Auswahl einer Aggregationsperiode device : einschließen oder ausschließen von Datensätzen die <device> enthalten + limit : begrenzt NUR die Anzahl der anzuzeigenden Datensätze reading : einschließen oder ausschließen von Datensätzen die <reading> enthalten readingNameMap : die entstehenden Ergebnisreadings werden partiell umbenannt seqDoubletsVariance : bis zu diesem Wert werden aufeinander folgende numerische Datensätze als identisch angesehen und werden gelöscht time.* : eine Reihe von Attributen zur Zeitabgrenzung + executeBeforeProc : ausführen FHEM Kommando (oder perl-Routine) vor Start des Befehls + executeAfterProc : ausführen FHEM Kommando (oder perl-Routine) nach Ende des Befehls valueFilter : ein zusätzliches REGEXP um die Datenselektion zu steuern. Der REGEXP wird auf das Datenbankfeld 'VALUE' angewendet. @@ -12621,7 +12904,6 @@ sub bdump { device : einschließen oder ausschließen von Datensätzen die <device> enthalten reading : einschließen oder ausschließen von Datensätzen die <reading> enthalten readingNameMap : die entstehenden Ergebnisreadings werden partiell umbenannt - seqDoubletsVariance : bis zu diesem Wert werden aufeinander folgende numerische Datensätze als identisch angesehen und werden gelöscht time.* : eine Reihe von Attributen zur Zeitabgrenzung valueFilter : ein zusätzliches REGEXP um die Datenselektion zu steuern. Der REGEXP wird auf das Datenbankfeld 'VALUE' angewendet.