tiger: Suchabfrage m.H.v. Perl und mySQL

Hallo,

ich habe da ein Problem. Ich soll über ein Perl-Skript eine Suchabfrage in einer Oracle-Datenbank machen.
Dazu habe ich ein Skript geschrieben. Die Suchabfrage findet in der Subroutine Suchen statt:
 - einer oder zwei der Werte von $nummer, $tsskennung, $eMail wurden zuvor in ein Formular geschrieben und dieses Formular ruft bei abschicken das perl-Skript auf, in welchen es die Subroutine "Suchen" gibt:

sub Suchen {
 my $nummer = shift;
 my $tsskennung = shift;
 my $eMail = shift;

use DBI;

$$datasource = $data_source::datasource;  # Zugangsdaten zur DB -> Quelle
 $dbusername = $data_source::dbusername;  #      -> User
 $dbpassword = $data_source::dbpassword;

$dbh = DBI->connect($datasource, $dbusername, $dbpassword, {RaiseError =>1, AutoCommit =>0}) || die "Unable to connect: $DBI::errstr";

my ($SQL,$sth);
 if ($nummer && $eMail eq ''){
  $SQL = "SELECT * FROM $computer_names WHERE tsslogin like '$tsskennung'";
  }

elsif ($tsskennung && $eMail eq ''){
  $SQL = "SELECT * FROM $computer_names WHERE computernumber like '$nummer'";
  }
 elsif ($nummer && $tsskennung eq ''){
  $SQL = "SELECT * FROM $computer_names WHERE respemail like '$eMail'";
  }

elsif ($nummer eq ''){
  $SQL = "SELECT * FROM $computer_names WHERE tsslogin like '$tsskennung '";
  }

elsif ($tsskennung eq ''){
  $SQL = "SELECT * FROM $computer_names WHERE respemail like '$eMail'";
  }

elsif ($eMail eq ''){
  $SQL = "SELECT * FROM $computer_names WHERE computernumber like '$nummer'";
  }
 else  { print "Es muss mindestens ein Feld ausgefuellt sein!\n"}

$sth = $dbh->prepare($SQL) || die "Unable to connect: $DBI::errstr";
  $sth->execute();

while (@row = $sth->fetchrow_array) {
   $nummer     = @row[0];
       $tsskennung = @row[1];
      $eMail      = @row[2];
}
$sth->finish;

return @row = ($nummer, $tsskennung, $eMail);

$dbh->disconnect();

}

Diese Subroutine wird wie folgt aufgerufen:

$Suchen = uc(&Suchen($nummer ,$tsskennung ,$eMail));

Ist das alles so richtig?
Mein spezielles Problem dabei ist jetzt, dass ich nicht weiß wie die Suchergebnisse ausgegeben werden (eigentlich doch mit ...(@row = $sth->fetchrow_array... usw.) Stimmt das? Wenn ja, wie erreiche ich, dass mir meine Suchergebenisse als HTML-Code an den Browser gesendet werden?
Ich hoffe mir kann jemand weiter helfen - diese Problem beschäftigt mich schon fast ne Woche!

Gruss
Tiger

  1. Hallo,

    use DBI;

    So etwas solltest Du gleich am Anfang des Scripts einfügen, es wird ja sowieso beim Start ausgeführt  (siehe perldoc -f use).

    my ($SQL,$sth);

    Variablen in Großbuchtsaben sind nicht gerade guter Perl-Stil (siehe dazu perldoc perlstyle).

    if ($nummer && $eMail eq ''){
      $SQL = "SELECT * FROM $computer_names WHERE tsslogin like '$tsskennung'";
      }

    [...]

    else  { print "Es muss mindestens ein Feld ausgefuellt sein!\n"}

    Ich finde, daß das rechtungeschickt und unübersichtlich ist.
    Denke einmal über folgende Code nach (der überdies auch mehrere Kritierien gleichzeitig berücksichtigt) :

    my $wherestring = '';
    if($nummer ne '')
       {
       $wherestring .= (($wherestring eq '')?' WHERE ':' AND ')." COMPUTERNUMBER = $nummer ";
       }
    if($tsskennung ne '')
       {
       $wherestring .= (($wherestring eq '')?' WHERE ':' AND ')." TSSLOGIN = '$tsskennung' ";
       }
    if($email ne '')
       {
       $wherestring .= (($wherestring eq '')?' WHERE ':' AND ')." RESPEMAIL = '$email' ";
       }
    if($wherestring eq '')
       {
       return 'es muß mindestens ein Feld angegben sein';
       }

    my $sql = "SELECT COMPUTERNUMBER,TSSLOGIN,RESPEMAIL from $computer_names $wherestring";

    my $dbh = DBI->connect($datasource, $dbusername, $dbpassword, {RaiseError =>1, AutoCommit =>0}) || die "Unable to connect: $DBI::errstr";

    1.) LIKE solltest Du nur verwenden, wenn Du mit Teilstrings suchst (FELD LIKE 'TEST%');
    2.) Unterscheide zwishcen numerischen und String-Vergleichen. zweiteres erforder von der Datenbank Typkonvertierungen, welche sich schlecht auf die Leistung auswirken
    3.) bei SELECT-Statements solltest Du immer die gewünschten Felder anführen. 'SELECT * FROM ...' solltest Du nur im Entwicklungsumfeld benutzen. Damit kannst Du garantieren, dßa die Felder auhc in der von Dir gewünschten Reihenfolge geliefert werden.
    4.) Den Datenbankconnect solltest Du immer _nach_ sämtlichen Prüfungen machen, da er auch Performance kostet und Dir mehr Arbeit beim Aufräumen im Fehlerfalle macht.
    5.) ich gehe eimal davon aus, daß $comuter_names den Namen der gewünschetn Tabelle irgendwo anders im Script definiert.

    $sth = $dbh->prepare($SQL) || die "Unable to connect: $DBI::errstr";

    ^^^^^^^
    Cut&Paste ist ein Schwein;-)

    while (@row = $sth->fetchrow_array) {
       $nummer     = @row[0];
           $tsskennung = @row[1];
          $eMail      = @row[2];
    }

    Einzelwerte aus einem Array werden üblicherweise mit $row[0] usw. abgefragt. @row[0] ist eine Liste und kein Scalar, das ist ein wesentlicher Unterschied.

    while() läßt vermuten, dßa es auch durchaus mehrere Datensätze geben kann, die den Suchkriterien entsprechen. Wie willst Du das berücksichtigen? Derzeit wird nur der zuletzt von der Datenbank gelieferte Datensatz weiter verarbeitet.

    return @row = ($nummer, $tsskennung, $eMail);

    die Zuweisung auf @row ist hier unnötig.

    $dbh->disconnect();

    Diese Anweisung wird nie und nimmer ausgeführt, und kann, je nach Treiber einen Fehler produzieren. Zumindest wird es aber unnötige Resourcen binden.

    Ist das alles so richtig?

    Mein spezielles Problem dabei ist jetzt, dass ich nicht weiß wie die Suchergebnisse ausgegeben werden (eigentlich doch mit ...(@row = $sth->fetchrow_array... usw.) Stimmt das? Wenn ja, wie erreiche ich, dass mir meine Suchergebenisse als HTML-Code an den Browser gesendet werden?

    $Suchen = uc(&Suchen($nummer ,$tsskennung ,$eMail));
    warum host Du dir nicht die Einzelwerte ab und formatierst die im aufrufenden Code?

    ($nummer ,$tsskennung ,$eMail)= &Suchen($nummer ,$tsskennung ,$eMail);
    print <<EOT;
    Folgende Daten wurden gefunden:<br>
    Nummer: $nummer<br>
    Kennung: $tsskennung<br>
    Email: $eMail<br>
    EOT

    Grüße
      Klaus

    1. Hallo Klaus,
      erstmal vielen vielen Dank, dass du dir so viel Zeit für mein Skript genommen hast.
      Ich habe deine Anregungen mit einbezogen und wieder getestet. Aber irgendwie werden mir immer noch keine Suchergebnisse ausgegeben.
      Die SQL-Abfrage klingt super, funktioniert auch, denke ich, aber kannst du die folgende Zeile vielleicht nochmal für mich kommentieren? Irgendwie ist mir das zu hoch ;-)

      {
         $wherestring .= (($wherestring eq '')?' WHERE ':' AND ')." COMPUTERNUMBER = $nummer ";
         }

      Das folgende verstehe ich immer noch nicht so ganz, dass ich $row[0] usw. schreiben muss verstehe ich. Aber ist diese while-Schleife jetzt erforderlich??

      while (@row = $sth->fetchrow_array) {
         $nummer     = $row[0];
             $tsskennung = $row[1];
            $eMail      = $row[2];
      }

      while() läßt vermuten, dßa es auch durchaus mehrere Datensätze geben kann, die den Suchkriterien entsprechen. Wie willst Du das berücksichtigen? Derzeit wird nur der zuletzt von der Datenbank gelieferte Datensatz weiter verarbeitet.

      Es gibt auf jeden Fall mehrere Datensätze, die den Suchkriterien entsprechen. Wie kann ich so etwas berücksichtigen??

      return @row = ($nummer, $tsskennung, $eMail);
      die Zuweisung auf @row ist hier unnötig.

      also reicht hier return @row aus??

      $dbh->disconnect();

      Diese Anweisung wird nie und nimmer ausgeführt, und kann, je nach Treiber einen Fehler produzieren. Zumindest wird es aber unnötige Resourcen binden.

      Wieso wird diese Anweisung nicht ausgeführt?

      $Suchen = uc(&Suchen($nummer ,$tsskennung ,$eMail));
      warum host Du dir nicht die Einzelwerte ab und formatierst die im aufrufenden Code?

      ($nummer ,$tsskennung ,$eMail)= &Suchen($nummer ,$tsskennung ,$eMail);
      print <<EOT;
      Folgende Daten wurden gefunden:<br>
      Nummer: $nummer<br>
      Kennung: $tsskennung<br>
      Email: $eMail<br>
      EOT

      »»
      Das habe ich probiert, aber irgendwie kommen keine Suchergebnisse raus, obwohl das ganze vom Syntax her richtig ist.
      Soll ich evtl. mal das vollständige Skript posten?

      Viele Grüße
      tiger

      1. Hallo,

        Die SQL-Abfrage klingt super, funktioniert auch, denke ich, aber kannst du die folgende Zeile vielleicht nochmal für mich kommentieren? Irgendwie ist mir das zu hoch ;-)

        $wherestring .= (($wherestring eq '')?' WHERE ':' AND ')." COMPUTERNUMBER = $nummer ";

        Pass auf, es ist jetzt bidde folgendes:

        Ein Select-Statement mit einem Suchkriterium sieht ja so  aus
        SELECT blablabla FROM table
          WHERE feld1 = 123

        Willst Du zwei oder mehr Kriterien berücksichtigen, sieht das dann so aus:

        SELECT blablabla FROM table
          WHERE feld1 = 123
            AND feld2 = 'wasauchimmer'
            AND feld3 = 'nochwas'

        (wobei ich jetzt davon ausgehe, daß alle Kriterien erfüllt sien müssen, was durch die AND's ausgedrückt ist)

        Lassen wir jetzt einmal die erste Zeile weg, ist
          WHERE feld1 = 123
            AND feld2 = 'wasauchimmer'
            AND feld3 = 'nochwas'
        ja das fertige Suchkriterium über drei Felder.
        Eigentlich haben all diese Zeilen ( == Einzelkriterium) das gleiche Muster:
        Die erste Zeile beginnt mit WHERE, alle anderen mit AND, und dann kommt der Vergleich 'FELDNAME = WERT'. In meinem Code hänge ich, falls ein Kriterium vom Benutzer angegeben wurde, dieses einfach an die Variable $wherestring nach dem genannten Schema an ($wherestring .=) (beachte den Punkt vor dem Gleichheitszeichen). Jetzt muß ich bei jedem Einzelkriterium nur erkennen, ob es das erste ist, oder nicht. Das kann ich feststellen, indem ich überprüfe ob schon was in der Variable steht ($wherestring eq ''). Wenn ja, dann nehme ich WHERE, sonst AND. Das mach das Konstrukt (($wherestring eq '')?' WHERE ':' AND '), der Rest (." COMPUTERNUMBER = $nummer ") hängt noch den eigentlcihen Feld-Vergleich an.

        Das ist einfacher zu programmieren, als zu erklären:-(

        ... dass ich $row[0] usw. schreiben muss verstehe ich.

        gut;-)

        Aber ist diese while-Schleife jetzt erforderlich??...

        Tja...

        Es gibt auf jeden Fall mehrere Datensätze, die den Suchkriterien entsprechen.

        ... aha *g* ...

        Wie kann ich so etwas berücksichtigen??

        Indem Du beispielsweise die Daten in einer while()-Schleife anrufst;-)

        Dein Problem ist ja immer noch, daß DU nicht weißt, wie Du die Daten ausgeben willst.
        Dazu solltest Du vielleicht einmal eine Statische HTML-Seite machen, die Dir als Vorlage für die Ausgabe dienen kann. Dann mußt Du noch entscheiden, ob Deine Suchfunktion direkt etwas ausgeben soll oder nicht. Beide Varianten haben ihre Vor- und Nachteile. Wenn sie direkt etwas ausgibt, dann kann sie wahrscheinlich sinnvollerweise nur zu einem bestimmten Zeitpunkt aufgerufen werden.

        das könnte dann vielleicht (ganz grob) so aussehen:
        print '<table>';
        while (@row = $sth->fetchrow_array) {
            print '<tr><td>';
            print join('</td><td>',@row);
            print '</td></tr>';
           }
        print '</table>';

        Wenn Du die Daten aber erst im aufrufenden Programm ausgeben willst, mußt Du sie zwangsläufig zwischenspeichern, was den u.a. Nachteil hat, daß es eventuell sehr viel Speicher benötigt, wenn viele Daten gefunden werden.
        Das könnte dann so aussehen

        my @result = ();
        while (@row = $sth->fetchrow_array) {
           push @result, [@row]
           }

        $sth->finish;
        $dbh->disconnect;
        return @result;

        Wie du mit solchen Datenstrukturen weiter vorgehen kannst, kannst Du nachlesen, und zwar bei
        perldoc perllol
        und
        perldoc perldsc

        return @row = ($nummer, $tsskennung, $eMail);
        die Zuweisung auf @row ist hier unnötig.

        also reicht hier return @row aus??

        Ich dachte  eher an
        return ($nummer, $tsskennung, $eMail);

        $dbh->disconnect();
        Wieso wird diese Anweisung nicht ausgeführt?

        Weil Du vorher schon mit
        return;
        aus der Funktion in das aufrufende Programm zurückkehrst.

        Soll ich evtl. mal das vollständige Skript posten?

        Nein, lieber nicht, wenn dann stelle sie irgendwo als Text online, und poste hier nur den Link.

        Grüße
          Klaus

  2. Hi tiger,

    elsif ($tsskennung && $eMail eq ''){
      $SQL = "SELECT * FROM $computer_names WHERE computernumber like '$nummer'";
      }
    elsif ($nummer && $tsskennung eq ''){
      $SQL = "SELECT * FROM $computer_names WHERE respemail like '$eMail'";
      }

    Mir erscheint Deine Parameterversorgung verbesserungsfähig.

    Was ist, wenn _mehr_ als ein Wert nicht-leer ist? Damit wird Deine Logik nicht fertig ... und falls dieser Fall nicht auftreten kann, übergibst Du zu viele Informationen.

    Daß LIKE erstens wildcards braucht (Deine Queries werden in der vorliegenden Form nichts finden) und zweitens furchtbar langsam sein kann (keine Indexzugriffe), weißt Du?

    return @row = ($nummer, $tsskennung, $eMail);

    Diese Zeile habe ich nicht verstanden.

    Wenn ja, wie erreiche ich, dass mir meine Suchergebenisse als HTML-Code an den Browser gesendet werden?

    Indem Du sie nach stdout ausgibst. Die Kommunikation übernimmt der Webserver, der Dein Programm über die CGI-Schnittstelle aufgerufen wurde.
    (Vergiß nicht, einen korrekten HTTP-Header vor dem HTML-Code auszugeben ...)

    Viele Grüße
          Michael

    --
    T'Pol: I apologize if I acted inappropriately.
    V'Lar: Not at all. In fact, your bluntness made me reconsider some of my positions. Much as it has now.
    (sh:| fo:} ch:] rl:( br:^ n4:( ie:% mo:) va:| de:/ zu:| fl:( ss:) ls:~ js:|)
    1. Hi Michael,
      danke für deine Hilfe.
      Mit "return @row = ($nummer, $tsskennung, $eMail);" sollen die Suchergebenisse, die in $nummer usw. gespeichert sind, an das Hauptprogramm übergeben werden. Ist das falsch?

      1. Hi tiger,

        Mit "return @row = ($nummer, $tsskennung, $eMail);" sollen die Suchergebenisse, die in $nummer usw. gespeichert sind, an das Hauptprogramm übergeben werden. Ist das falsch?

        wozu brauchst Du dabei @row?

        Viele Grüße
              Michael

        --
        T'Pol: I apologize if I acted inappropriately.
        V'Lar: Not at all. In fact, your bluntness made me reconsider some of my positions. Much as it has now.
        (sh:| fo:} ch:] rl:( br:^ n4:( ie:% mo:) va:| de:/ zu:| fl:( ss:) ls:~ js:|)