bert: Spaltennamen einer SQL-Abfrage auslesen

Ich habe eine MySQL-Datenbank, in der verschiedene Auswertungen gebraucht werden. Die SQL-Statements können dabei konfiguriert werden und völlig verschiedene Spalten und Daten liefern. Ich generiere jeweils XLS-Dateien daraus. Hier ein Codeschnipsel:

$QueryObject = $db->prepare($sql);
$QueryObject->execute();
while ( $zref = $QueryObject->fetchrow_hashref()) {
 if ($Counter == 0) {
   #Überschriften auslesen
   @KeyList = keys % {$zref};
   foreach (@KeyList) {
        #Spaltennamen in XLS-Datei schreiben
 $Worksheet->write($RowNum, $ColNum, $_ , $FormatHeadline);
 $ColNum++;
   }
   $ColNum = 0;
   $RowNum++;
 }
 #Daten
 @KeyList = keys % {$zref};
 foreach (@KeyList) {
   #Spaltenwerte exportieren
   $Worksheet->write($RowNum, $ColNum, $$zref{"$_"} ,$FormatContent);
   $ColNum++;
  }
  $Counter++;
  $ColNum = 0;
  $RowNum++;
}

Dem Script soll es egal sein, welche Spalten über $sql abgefragt werden. Die Spaltennamen brauche ich aber als Überschrift in der XLS-Datei.
Ich verwende fetchrow_hashref, damit ich die Spaltennnamen der Anfrage kriege und diese über keys %{zref} auslesen kann.
Das funzt eigentlich ganz gut, jedoch kommen die Spalten in zufälliger Reihenfolge an, und nicht in der, die im SQL festgelegt wurde. Vermutlich liegt das an der internen Verwaltung des Hashes.

Wie kriege ich die Spaltennamen einer Abfrage in die richtige Reihenfolge?

Bert

  1. hi,

    Wie kriege ich die Spaltennamen einer Abfrage in die richtige Reihenfolge?

    $ary_ref  = $dbh->selectrow_arrayref("describe table");

    Im array dann auf [0] gucken.

    Viele Grüße,
    Hotte

    1. hi,

      Wie kriege ich die Spaltennamen einer Abfrage in die richtige Reihenfolge?

      $ary_ref  = $dbh->selectrow_arrayref("describe table");

      Tschuldige, das war Käse. Hier stehts richtig:

      my $ref  = $dbh->selectall_arrayref("describe table");
      my @cols = map{$_->[0]}@$ref; # array mit Spaltennamen
      print join("\n", @cols), "\n"; # zur Kontrolle

      -- Hotte

      1. Hallo Hotte,

        danke für die schnelle Antwort. Leider ist das noch nicht ganz das, was ich suche. Ich frage nicht einfach eine Tabelle ab, sondern ich habe ein SELECT-Statement, das dynamisch konfiguriert wird. Ich kenne es also nicht vorher, es kann aber recht lang sein, hier mal ein "kurzes" als Beispiel:

        select Date_Format(FROM_UNIXTIME(v.Zeitstempel) , "%d.%m.%Y") Datum, l.Name Filiale,
             sum(if(a.name='Jacke', p.Anzahl , 0)) 'Anzahl Jacken',
             sum(if(a.name='Hose', p.Anzahl , 0)) 'Anzahl Hosen',
             sum(if(a.name='Schuh', p.Anzahl , 0)) 'Anzahl Schuhe'
          from verkauf v LEFT JOIN verkauf_positionen p using (VerkaufsID)
          left join map_locations l on (l.LocationID=v.locationID)
          left join artikel a on (p.ArtikelID=a.ArtikelID)
         where v.ENTRYTYPE = 'ACTIVE' and p.EntryType = 'ACTIVE' and l.EntryType = 'ACTIVE'
         and (v.Status = 200 or v.Status = 300)
         and (FROM_UNIXTIME(v.Zeitstempel) >= STR_TO_DATE('01.12.2007',"%d.%m.%Y")) and (FROM_UNIXTIME(v.Zeitstempel) < STR_TO_DATE('04.03.2008',"%d.%m.%Y"))
         group by v.VerkaufsID

        Meistens sind die Statements noch lustig verschachtelt usw. Jedenfalls ergibt dieses SELECT was in der Form (Datum, Filiale, Jacken, Hosen, Schuhe).
        Mit meinem Script krieg ich in meiner Datei sowas angezeigt (Jacken, Datum, Schuhe, Filiale, Hosen), also ziemlich durcheinander.

        my $ref  = $dbh->selectall_arrayref("describe table");
        my @cols = map{$_->[0]}@$ref; # array mit Spaltennamen

        Im Prinzip kann ich ein DESCRIBE auch auf ein SELECT anwenden, das ergibt aber etwas anderes.

        Noch andere Ideen?

        1. Ich frage nicht einfach eine Tabelle ab, sondern ich habe ein SELECT-Statement, das dynamisch konfiguriert wird.

          Was bitteschön ist "dynamisch konfiguriert"?

          Ich kenne es also nicht vorher, es kann aber recht lang sein

          Aber an irgendeiner Stelle der "dynamischen Konfiguration" wirst Du doch die Feldliste haben, oder? Daraus kannst Du dann die Spaltenüberschriften bilden und brauchst dann später nur noch das Ergebnis von fetchrow_arrayref nacheinander abzuarbeiten.

          Siechfred

          --
          Hinter den Kulissen passiert viel mehr, als man denkt, aber meistens nicht das, was man denkt.
          1. Eigentlich sollte das eine ganz einfache Frage sein. In meiner Anwendung gibt es ein gutes Dutzend Auswertungen und vermutlich werden es bald noch mehr sein. Um den Programmcode nicht ändern zu müssen, habe ich für jede benötigte Auswertung eine sql-Datei in ein Verzeichnis gelegt. Der Nutzer kann dann eine aus dieser Liste auswählen und kriegt das Ergebnis als XLS-Datei.

            Im einfachsten Fall steht in einer solchen Datei: "SELECT * FROM TableXY" oder "SELECT a,b FROM viewXY"

            Für eine weitere Auswertung brauche ich (oder ein cleverer Nutzer) nur ein SQL-Statement zu formulieren und das als Datei abzulegen.

            Aber an irgendeiner Stelle der "dynamischen Konfiguration" wirst Du doch die Feldliste haben, oder? Daraus kannst Du dann die Spaltenüberschriften bilden und brauchst dann später nur noch das Ergebnis von fetchrow_arrayref nacheinander abzuarbeiten.

            Eigentlich ist über die Formulierung des SELECT ja alles über die Reihenfolge der Felder gesagt. Klar, ich kann in die Dateien zusätzlich noch eine Feldreihenfolge hinterlegen, z.B.:

            REIHENFOLGE:Feld1,Feld2,...
            SELECT * FROM TableXY

            Das ist doch aber doppelt gemoppelt! In anderen Programmiersprachen kenne ich das Problem nicht. Dort setze ich per ODBC oder sonstwie eine Abfrage ab und kriege die Werte und deren Bezeichnungen und deren Reihenfolge ganz einfach zurück.

            Ich bin recht neu in der PERL-Programmierung und dachte, hier müsste sich doch jemand mit sowas auskennen.

            Hat denn wirklich noch keiner eine allgemeingültige Routine zum Export aus MySQL mit Perl geschrieben?

            1. Im einfachsten Fall steht in einer solchen Datei: "SELECT * FROM TableXY" oder "SELECT a,b FROM viewXY"

              Also wird da nix dynamisch zusammengesetzt, sondern das Statement wird aus einer Textdatei ausgelesen. Damit kannst Du aus dem Statement doch ganz einfach die Feldliste extrahieren, entweder mit Regulären Ausdrücken oder indem Du das NAME-Attribut Deines Statement-Handles abfragst (siehe Beispiel in der verlinkten Doku).

              Hat denn wirklich noch keiner eine allgemeingültige Routine zum Export aus MySQL mit Perl geschrieben?

              Das DBI-Modul bietet eigentlich alles, was man braucht. Btw, zum dynamischen Generieren von SQL-Abfragen schau Dir mal SQL::Abstract an. Stell' Dir mal vor, einer Deiner Nutzer verhaspelt sich beim Statement - und schon fliegt Dir Dein Programm um die Ohren.

              Siechfred

              --
              Hinter den Kulissen passiert viel mehr, als man denkt, aber meistens nicht das, was man denkt.
              1. Danke Alexander und Siechfred,

                mit NAME geht es super. Hab mir doch gedacht, dass es was in der Art geben muss.

                Bert

                #################################################################
                while (@row = $sth->fetchrow_array) {
                  if ($Counter == 0) {
                    #Überschrift
                        foreach (@{$sth->{NAME}}) {
                            $Worksheet->write($RowNum, $ColNum, $_ , $FormatHeadline);
                            $ColNum++;
                          }
                        $ColNum = 0;$RowNum++;
                    }
                  #Daten
                  foreach (@row) {
                    $Worksheet->write($RowNum, $ColNum, $_ , $FormatContent);
                    $ColNum++;
                  }
                  $Counter++;
                  $ColNum = 0;$RowNum++;
                }

            2. Moin Moin!

              http://search.cpan.org/~timb/DBI-1.602/DBI.pm#NAME_(array-ref,_read-only)

              Alexander

              --
              Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".