Perl und Hash

ghostadmin

Grand Admiral Special
Mitglied seit
11.11.2001
Beiträge
25.213
Renomée
191
Standort
Dahoam Studios
Vor lauter Hash, Hashref, Array, hash in hash blicke ich irgendwie nicht mehr durch.

ganz grob: Ich habe eine Tabelle in einer MySQL DB, ich möchte von dort bestimmte (auch mehrere) Zeilen (Name,Telefon,Mobil) in ein Hash oder Array speichern.

Da habe ich bis jetzt:

Code:
my $sth = $dbh->prepare("SELECT name,mobile,homenumber from $mysql_table WHERE name like ?"); 
			$sth->execute('%'.$argcallerid.'%');
			my $sqlsearchresult = $sth->fetchall_hashref('name'); #put all into hash grouped by name
			$sth->finish();
			$dbh->disconnect();

Ist das überhaupt so richtig?

Erscheint das in der Form?
my %sqlsearchresult = (
namexy => {
mobile => "123",
homenumber => "321"
};

Ich möchte anhand der Nummer in einer Anrufliste herausfinden welcher Name da angerufen hat. Will also im Hash sehen welcher Nummer welcher Name zugeordnet ist.
 
Zuletzt bearbeitet:
Ah, DB-Operationen hatte ich mit Perl noch nicht.

Also deine Frage lässt sich eigentlich hiermit schön beantworten, das ist auch optisch schön aufbereitet:
http://stackoverflow.com/questions/3573370/perl-dbi-fetchall-hashref

Dein Anwendungsfall klingt aber eher so, also solltest du über die Ergebniszeilen iterieren, mit fetchrow_hashref. Dein Ergebnis-Hash müsstest du dann selbst aufbauen.
Da du 2 Telefonnummer-Spalten hast, wird das keine Funktion out-of-the-box liefern.
 
hui ich weiß nicht ob ich sowas überhaupt hinbekomme, momentan scheitere ich schon das mit dumper auszugeben.

print Dumper($sqlsearchresult);

da erscheint dann immer nur undef

Oder ist das mit Array einfacher?

Edit:
http://www.virtualhelp.me/scripts/56-perl/578-iterate-through-hash-of-hashes
Das müsste doch gehen aber für was steht da $hash und warum ist das nicht deklariert?
 
Zuletzt bearbeitet:
Also bei dem Link sehe ich - sofern meine PerlZeit nicht zu lange her ist - mehrere syntaktische Fehler. Scheint mir keine gute Quelle zu sein.
Perl ist zwar tolerant, aber ein guter Stil zahlt sich aus.

Ich kann jetzt nicht dein Problem lösen, aber das hier sollte dich weiterbringen:
http://www.infodrom.org/~joey/Writing/freeX/perl5/

Relativ weit unten ist eine Besipielschleife mit fetch_hashref:
Code:
   while ($row = $sth->fetchrow_hashref) {
     printf " %-40s %s\n", $row->{'Vorname'} . " " . $row->{'Nachname'},
         $row->{'PLZ'} . " " . $row->{'Stadt'};
   }
Du bekommst da pro Zeilenschleife eine Zeile deiner Ergebnismenge und kannst diese dann bearbeiten.

--
Vll ein kurzer Auszug zu Hashrefs oder Refs allgemein: Perl kennt 3 Datentypen: Skalare ($), Listen (@) und Hashes (%).
Listen und Hashes dürfen nur Skalare enthalten.
Wie bekommt man nun einen Hash in einen Hash? Man nimmt die Referenz (ziemlich genau dasslebe wie ein Pointer/Zeiger in C) auf den Hash und legt diesen darin ab. Referenzen gelten als Skalare.
Ein paar kurze Syntax-Beispiele, aber mit zur Einfachheit mit Listen:
Liste: @myList
Erster Element der Liste: $myList[0]
Referenz auf die Liste: \@myList

Zugriff auf das erste Element einer Liste über die Referenz der Liste: $myListRef->[0]

Für Hashes in Prinzip dann dasselbe, nur eben mit {}-Klammern.

Hoffe das hilft.
 
Das geht leider nicht, ich muss die MySQL Abfrage getrennt machen, kann also nicht während dessen die Suche machen weil ich eine externe Datei Zeile für Zeile parse und da nicht für jede Zeile ne DBI Verbindung machen möchte.

Was ist der Zweck dieser Referenzen?
$href = \%hash; # $href now holds a reference to %hash
 
Das Problem, dass du nebenher noch eine Datei einliest, hättest du im anderen Fall doch auch? Ich weiß jetzt nicht, was die Schleife daran ändert.
Aber wie gesagt, ich kenne die Problemstellung ja nicht, ab hier bist du gefordert.

Zweck der Referenzen:
Ich kenne genau 2. Einerseits verwendet man die beim Aufruf von Subroutinen, damit nicht komplexe Datenstrukturen auf den Stack kopiert werden (eine Performance-Sache) und (der Punkt trifft hier zu) um Hashes in Hashes ablegen zu können.
Das hier geht nicht: $myHash{'key'} = %innerHash;
Aber das hier geht: $myHash{'key'} = \%innerHash;
 
Weil das ganze ne recht komplexe Struktur hat, habe ich die Erklärung bisher weggelassen.

Also das ganze ist so, ich habe eine Telefonanrufliste die Zeile für Zeile geparsed und im Web dargestellt wird.
Bei unbekannten Nummern die keinen Telefonbucheintrag haben, wird einmalig ein Reverslookup im Netz gemacht und danach in der Asterisk DB (Name) sowie in MySQL (Adresse) zwischengespeichert, damit man weniger http Abfragen hat. Ich benutze gleichzeitig Asterisk DB und MySQL weil die Asterisk DB für die Anzeige der Rufnummer am Telefon dient und man dort keine weiteren Felder wie Strasse etc. speichern kann.

Bei der Darstellung der Tabelle ins Web soll, wenn in der Asterisk DB kein Eintrag zu der Nummer ist, die Namen und optional Adresse aus der MySQL DB (also die vom Reverselookup im Internet stammen) ausgelesen werden und da habe ich bis jetzt für jede Abfrage einen MySQL Select drin, den ich vermeiden möchte.

Nun gibt es ein Suchfeld für den Namen, danach soll nur einmalig eine MySQL Abfrage gemacht werden (siehe oben mit Like). Die Abfrage wird in den Hash (oder wahlweise ein Array) gespeichert und wenn die Tabelle im Web generiert wird, soll bei jeder Zeile gesucht werden ob es für die Nummer einen Eintrag im Hash oder Array gibt.
Ansonsten müsste ich ja wieder bei jeder dargestellten Row ne MySQL Abfrage machen.
Oder anders ausgedrückt, während die MySQL Abfrage läuft, habe ich ja noch keine Zeilen mit Content dargestellt weil das nach und nach geparsed wird.

Also was ich machen möchte ist nur einen einzigen Select für die normale Darstellung und nur einen Select für die Suche.
Die Asterisk DB wird bereits nur einmalig ausgelesen und in einen Hash gespeichert aber da war es einfach weil es da nur key und value gibt, kein hash in hash.

Das mit der Abfrage aus dem Hash der von MySQL stammt, müsste irgendwie mit 2 for each und/oder while gehen aber ich bekomm das nicht zusammen.
 
Zuletzt bearbeitet:
Also mit dem Schleifenkonstrukt aus meinem Vorschlag würde auch nur ein Select gemacht werden, man iteriert über das Ergebnis, das ein einzelnes Statement zurückliefert.
Außerdem hättest du innerhalb der Schelife kein Hash-in-Hash, sondern nur ein Hash, in dem pro Spalte ein Key-/Value-Paar drinsteckt.

Mein Empfehlerung wäre hier, dir eine eigene Datenstruktur zu überlegen, die du sinnvoll brauchen könntest.
Dann würde ich die Daten aus den DBs auslesen, in deine Datenstruktur konvertieren und auf Basis deiner Datenstruktur dann das Rendering erledigen.
Falls sich dein DB-Aufbau mal ändert, müsstest du dann nur den ersten Teil anpassen (Auslesen und Konvertierung), falls sich das Rendering ändert nur den letzten Teil (Einlesen + Rendering).

Viel Erfolg :)
 
Also mit dem Schleifenkonstrukt aus meinem Vorschlag würde auch nur ein Select gemacht werden, man iteriert über das Ergebnis, das ein einzelnes Statement zurückliefert.
Außerdem hättest du innerhalb der Schelife kein Hash-in-Hash, sondern nur ein Hash, in dem pro Spalte ein Key-/Value-Paar drinsteckt.

Der eine Select bringt mir aber nichts. Ich brauche die Abfrage ganz wo anders im Code, da ist die DB Verbindung schon längst geschlossen.
Ich vermute das die Fragestellung immer noch nicht ganz klar ist, ich möchte bei einem bereits erfolgten Select der in einen Hash-in-Hash gespeichert wurde, die Daten auslesen.

---------- Beitrag hinzugefügt um 15:50 ---------- Vorheriger Beitrag um 15:16 ----------

So jetzt hab ichs raus!

Code:
#!/usr/bin/perl

use warnings;
use strict;

use DBI;
use Data::Dumper;
my $cdrnumber = "0123"; #nur als beispiel, nummer aus cdrlog
my $dbh = DBI->connect("DBI:mysql:database=asteriskpb;host=localhost","asterisk","pass") or die $DBI::errstr;
					
my $sth = $dbh->prepare("SELECT name,mobile,homenumber from phonebook WHERE name like ?"); 
$sth->execute('%'.'zusuchendername'.'%');
my $sqlsearchresult = $sth->fetchall_hashref('name'); #put all into hash grouped (key) by name
$sth->finish();
$dbh->disconnect();
		
#print Dumper($sqlsearchresult);

my %sqlsearchresultnew = %$sqlsearchresult; #dereference

# for loop die die Zeilen aus cdrlog generiert

if ( $sqlsearchresult ne "") {  
#nach nummer aus cdrlog suchen die gleich mobile,homenumber aus mysql ist

	foreach my $name (keys %sqlsearchresultnew) {
		while (my ($key, $value) = each %{ $sqlsearchresultnew{$name} } ) {
			#print "$name $key $value \n";
			if ((defined $value) && ($cdrnumber eq $value)) {
				print "\n$name\n";
			}
		}

	}


}
# end loop
 
Jetzt hab ich mich noch gefragt ob man bei diesem Konstrukt auch zugehörige Strasse,PLZ etc. ausgeben kann innerhalb:

Code:
foreach my $name (keys %sqlsearchresultnew) {
		while (my ($key, $value) = each %{ $sqlsearchresultnew{$name} } ) {
			#print "$name $key $value \n";
			if ((defined $value) && ($cdrnumber eq $value)) {
				print "\n$name\n";
			}
		}

	}
 
Hallo,

zu deiner Frage: Das sollte eigentlich ab hier kein Problem mehr sein. Ohne Kontext ist es aber schwer, etwas genaueres dazu zu sagen. Aber wenn du soweit gekommen bist...

Ein paar Anmerkungen noch:
Code:
my %sqlsearchresultnew = %$sqlsearchresult; #dereference
Hier bist du auf eine Eigenheit von Perl gestoßen, die (imho) unschön ist: Es findet bei dieser Zeile keine reine Dereferenzierung statt, sondern es wird der Hash komplett kopiert!
Eine Nebenwirkung des Zuweisung-Operators. Also "%$" ist schon richtig, aber beim "=" wird der dereferenzierte Hash auf der rechten Seite komplett kopiert und die Kopie dem Wert auf der linken Seite zugewiesen.
Ist für deinen Fall vermutlich nicht schlimm, aber zumindest nicht das, was man erwartet. Ich würde mir gerade aus diesem Grund den Pfeiloperator angewöhnen. Also $hashref->{key} anstelle von %$hashref{key}.

Ansonsten sehe ich, dass in deinem SELECT keine Straße und PLZ auftaucht. Fehlen die vielleicht deshalb im Hash?
 
Also die extra Zeile habe ich auch nur weil die Foreach schleife aus einem Beispiel war und ich nicht wusste wie man das sonst reinschreibt. Auch ist mir immer noch nicht ganz klar was "Referenz" überhaupt bedeutet.
Und wo in den Loop genau Strasse, PLZ etc. reingehört, da kann ich auch nur raten und 100 Versuche machen bis es klappt (oder nicht).
Das es beim Select fehlt, ist klar das ist nicht das Problem.
 
Das es beim Select fehlt, ist klar das ist nicht das Problem.
So spontan würde ich sagen: Doch, das ist das grundlegende Problem. :) Solange es nicht aus der DB geholt wird, fehlt es auch in deiner Datenstruktur und dann kannst du lange raten, wie man da rankommt.

Auch ist mir immer noch nicht ganz klar was "Referenz" überhaupt bedeutet.
Du hattest bisher nichts mit Programmiersprachen zu tun?
Referenz ist nichts anderes als ein Zeiger (falls du dich mal mit C beschäftigt hast): Dein Hash liegt in irgendeinem Speicherbereich - du greifst mit %hash darauf zu. An irgendeiner anderen Stelle kann ein Zeiger liegen, der die Adresse des Hash-Speicherbereichs enthält. Um nun im Quellcode auf den Hash zuzugreifen, musst du die zusätzliche Indirektion angeben: $hashref-> - durch den -> Operator wird das leicht ermöglicht.
Der Sinn dahiner ist, dass man dadurch Speicher und Redundanzen spart. Möchtest du von einen Hash in einen anderen Hash packen, ohne Zeiger zu verwenden, müsstest du den kompletten Hash da rein kopieren. Dann hättest du den inneren Hash aber auch gleich doppelt: Einmal Standalone und einmal die Kopie in dem anderen Hash. Genau das kann man mit den Zeigern umgehen.
 
So spontan würde ich sagen: Doch, das ist das grundlegende Problem. :) Solange es nicht aus der DB geholt wird, fehlt es auch in deiner Datenstruktur und dann kannst du lange raten, wie man da rankommt.

hmm ne ich meinte das ich mir dessen schon bewusst bin.

Du hattest bisher nichts mit Programmiersprachen zu tun?

wenig, angefangen mit gwbasic, dann vb.net zu bash,php,perl aber nicht wirklich tief und alles selbst beigebracht.
 
Zurück
Oben Unten