Listenvergleich mit List::FullCompare

Max Maischein

Frankfurt.pm

Übersicht

  • Problemstellung

  • API

  • Algorithmus

  • Live demo

Wer bin ich?

  • Max Maischein

  • DZ BANK Frankfurt

  • Deutsche Zentralgenossenschaftsbank

  • Informationsmanagement TxB

Problemstellung

  • Handel macht Geschäfte mit anderen Banken

  • Abwicklung stimmt täglich Geschäftsbestand ab

  • intern (Handelssystem / Abwicklungssystem)

  • extern (Reports vom/an Kontrahenten)

Anforderungen

  • Zweiseitiger Vergleich

  • Vergleich von Geschäften

  • Gleiche Geschäftsnummern

  • Keine Abweichungen in den Feldern (z.B. Währung, Betrag, Menge, ...)

CPAN

Was bietet CPAN?

  • perlfaq4 / perldoc -q difference

  • List::Compare - nur einfache Arrays

Begriffsklärung

  • Geschäft - Item

  • Geschäftsnummer - Key

  • Wert(e) - Data

  • Rest

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Listen aushaken

Code

Eingangsdaten

 1:  use List::FullCompare;

Code

Eingangsdaten

 1:  use List::FullCompare;
 2:  my $left = [
 3:    { waehrung => 'EUR', betrag => 2, id=> 0 },
 4:    { waehrung => 'HUF', betrag => 4, id=> 0 },
 5:    { waehrung => 'EUR', betrag => 6, id=> 2 },
 6:  ];

Code

Eingangsdaten

 1:  my $left = [
 2:    { waehrung => 'EUR', betrag => 2, id=> 0 },
 3:    { waehrung => 'HUF', betrag => 4, id=> 0 },
 4:    { waehrung => 'EUR', betrag => 6, id=> 2 },
 5:  ];
 6:  my $right = [
 7:    { waehrung => 'EUR', betrag => 2, id=> 0 },
 8:    { waehrung => 'HUF', betrag => 4, id=> 0 },
 9:    { waehrung => 'TRL', betrag => 6, id=> 1 },
10:  ];

Code

 1:  my $result = List::FullCompare->compare(
 2:    left => $left,       # AoH
 3:    right => $right,     # AoH
 4:    keycols => ['id'],
 5:    valcols => ['waehrung'],
 6:    numcols => ['betrag'],
 7:  );

Code

 1:  my $result = List::FullCompare->compare(
 2:    left => $left,       # AoH
 3:    right => $right,     # AoH
 4:    keycols => ['id'],
 5:    valcols => ['waehrung'],
 6:    numcols => ['betrag'],
 7:  );
 8:  List::FullCompare::Excel->output(
 9:      comparison => $result,
10:      outname => 'test.xls',
11:  );

Ergebnis

Ergebnis

Ergebnis

Ergebnis

Ergebnis

Verfeinerung

  • Interne Reconciliation

  • durchgehende Referenz

  • Externe Reconciliation

  • Nicht unbedingt

Schrittweise Verfeinerung

  1. Alle gemeinsamen Referenzen

  2. Alle passenden Geschäfte nach Datum/Betrag/Währung

  3. Rest anschauen

Verfeinerung

 1:  my $result = List::FullCompare->compare(
 2:    left => $left,
 3:    right => $right,
 4:    keycols => [qw(id)],
 5:    valcols => ['datum','waehrung'],
 6:    numcols => ['betrag'],
 7:  );

Verfeinerung

 1:  my $result = List::FullCompare->compare(
 2:    left => $left,
 3:    right => $right,
 4:    keycols => [qw(id)],
 5:    valcols => ['datum','waehrung'],
 6:    numcols => ['betrag'],
 7:  );
 8:  $result->refine(
 9:    # keine Keys mehr
10:    keycols => [],
11:    valcols => ['datum','waehrung'],
12:    numcols => ['betrag'],
13:  );

Neuer Status

Numerische/String/Custom Komparatoren

 1:  key-left  key-right
 2:  dz-123    db-123
 3:  dz-124    db-124
 4:  dz-125    db-125

Numerische/String/Custom Komparatoren

 1:  key-left  key-right
 2:  dz-123    db-123
 3:  dz-124    db-124
 4:  dz-125    db-125
 5:
 6:  compare => {
 7:      key => sub {
 8:        my($l) = ($_[0]=~ /dz-(\d+)/)
 9:            or die "Invalid id $_[0]";
10:        my($r) = ($_[1]=~ /db(\d+)/)
11:            or die "Invalid id $_[1]";
12:        $l ne $r
13:      },
14:  }

Daten aus DBI

 1:  my $left = [
 2:    { waehrung => 'EUR', betrag => 2, id=> 0 },
 3:    { waehrung => 'HUF', betrag => 4, id=> 0 },
 4:    { waehrung => 'EUR', betrag => 6, id=> 2 },
 5:  ];

Daten aus DBI

 1:  my $left = $dbh->selectall_arrayref(
 2:    <<SQL,
 3:    { Slice => {} } );
 4:      select datum, id, waehrung, betrag
 5:      from transactions
 6:      where datum = '$gestern'
 7:      order by id
 8:  SQL

Algorithmus

  1. Sortiere alle Items in beiden Listen nach Key

  2. Für jeden Key

  3. Vergleiche das nächste Item links mit allen Items rechts

  4. Gibt es eine volle Übereinstimmung, nimm diese beiden Items und entferne sie aus den Listen

  5. Gibt es keine volle Übereinstimmung, hänge das linke Item an die Liste der Unpaare an

  6. Für die Liste der Unpaare nimm jeweils die beste Übereinstimmung

Anzahl der Vergleiche

  1. Für jedes Item links

  2. Vergleiche das Item mit allen Items rechts

Anzahl der Vergleiche

 1:  $n = @{ $items_links_key1 }; # Anzahl der Items
  1. Für jedes Item links

  2. Vergleiche das nächste Item links mit allen Items rechts

Anzahl der Vergleiche

 1:  $n = @{ $items_links }; # Anzahl der Items
  1. Für des Item links => $n -mal

  2. Vergleiche das nächste Item links mit allen Items rechts

Anzahl der Vergleiche

 1:  $n = @{ $items_links }; # Anzahl der Items
  1. Für des Item links => $n -mal

  2. Vergleiche das nächste Item links mit allen Items rechts => $n -mal

Anzahl der Vergleiche

 1:  $n = @{ $items_links }; # Anzahl der Items
  1. Für des Item links => $n -mal

  2. Vergleiche das nächste Item links mit allen Items rechts => $n /2 -mal

Anzahl der Vergleiche

 1:  $n = @{ $items_links }; # Anzahl der Items
  1. Für des Item links => $n -mal

  2. Vergleiche das nächste Item links mit allen Items rechts => $n /2 -mal

=> $n * ($n/2) Vergleiche im Durchschnitt

Anzahl der Vergleiche

 1:  $n = @{ $items_links }; # Anzahl der Items
  1. Für des Item links => $n -mal

  2. Vergleiche das nächste Item links mit allen Items rechts => $n /2 -mal

=> Rund $n ** 2 Vergleiche im Durchschnitt

Zeitverhalten

 1: Items Keys                NoKeys
 2:    10 0.00524401664733887 0.00486302375793457
 3:    50 0.0139620304107666  0.0106668472290039
 4:   100 0.0254371166229248  0.0192489624023438
 5:   500 0.12152099609375    0.1561279296875
 6:  1000 0.246109008789063   0.5
 7:  5000 1.29686903953552    9.59375
 8: 10000 2.8125              42.6875

Zeitverhalten

 1: Items
 2:    10
 3:    50
 4:   100
 5:   500
 6:  1000
 7:  5000
 8: 10000

Fazit

Quadratisches Wachstum

Ausgabeformat

  • HTML (-App)

  • Excel

Verwendete Module

  • Spreadsheet::Read - Lesen von Tabellen

  • Template::Toolkit - Ausgabe nach HTML

  • Spreadsheet::WriteExcel - Excel Ausgabe nach Excel

  • Dancer - für die Webanwendung

Beispielcode

Der Beispielcode ist online unter

(bald auf CPAN)

Danke

Der Beispielcode ist online unter

(bald auf CPAN)

Fragen?

Max Maischein (corion@cpan.org)

Automatische Erkennung von Spaltentypen

  • Datum ( mm/tt/yyyy, yyyy-mm-dd, dd.mm.yyyy, ... )

  • Betrag

  • Mit regulären Ausdrücken

Ausblick Recon 1.5

  • Bemerkungen

  • Fortschreibung

  • schließen für best. Zeit