Hi Nora

On 23/02/2011 12:53, HACKER Nora wrote:
> Hi,
> 
> I want to work with data from my database. The following select and
> dereferencing for display is fine:
> 
> my $sql = "select secondname || ' ' || firstname from supp_verantw_v";
> my $sth = $dbh->prepare($sql);
> my $personen = $dbh->selectall_arrayref($sql) or die "geht net: $!\n";

First of all, your prepare call is wasted here. You never use the $sth
again, and selectall_arrayref does its own prepare (and execute) in
order to fetch the data. You can just leave this line out or, if your
program performs this same select several times, it also accepts a
ready-prepared statement handle in place of an SQL string.

Also, I would be inclined to use DBI to fetch the raw data from the
database and let Perl manipulate it, rather than use the SQL engine
format the values for you. Something like:

  my $sql = 'select firstname, secondname from supp_verantw_v';  
  my $personen = $dbh->selectall_arrayref($sql) or die "geht net: $!";

  foreach my $person (@$personen) {
    my $fullname = $person->[1].' '.$person->[0];
    print $fullname, "\n";
  }

> my @personen = @$personen;
> foreach my $person ( @personen ) {
>      print "<p>Dereferencing: @$person\n</p>\n";
> }

There is no need to to copy the anonymous array to a named array. It is
wasteful of time and memory as it makes two identical instances of the
data in memory. Simply use @$personen everywhere you have @personen.

  foreach my $person (@$personen) {
    print "<p>Dereferencing: @$person\n</p>\n";
  }

> Now I want to alter the foreach-loop to a for-loop (because I need the
> index number) - but I don't know how to dereference over two levels:
> 
> for ( my $i = 0; $i<= $#personen; ++$i ) {
>      print "Person: $person->[$i]\n"; # ????????
> }

Elements of @$personen are accessed using $personen->[0],
$personen->[1]... etc. Each of these, in turn, are references to arrays,
and the elements of those arrays are reached with a second level of
indexing: $personen->[0]->[0], $personen->[0]->[1]... etc. Also Perl
allows us to remove indirection arrows beyond the first, so
$personen->[0][0], $personen->[0][1]... look much tidier. (Those two
values, by the way, are fields one and two of the first record returned.)

In addition, it is usual in Perl to iterate over a range of indices, so
your loop could be written:

  for my $i (0 .. $#{$personen}) {
    print "Person: $personen->[$i][0]\n";
  }

> Error: Using an array as a reference is deprecated at
> /opt/freeware/apache/share/cgi-bin/edit/supportumg_neu.pl line 57.
> Global symbol "$person" requires explicit package name at
> /opt/freeware/apache/share/cgi-bin/edit/supportumg_neu.pl line 57.

You are not 'using an array as a reference', so you must have seen this
error in a previous version of your code. And '"$person" requires
explicit package name' because it should be $personen!

> Bonus question: My main goal is to have a<select>  field in a HTML form.
> The data for the options comes from the above select, the "values" are
> meant to be the IDs from the same table. My approach was to issue two
> separate select statements, one for the persons' names, another one for
> their IDs - that's why I needed the for-loop for the index. I also read
> on CPAN's documentation of DBI that by using selectall_hashref it should
> be possible to store the corresponding ID and name together in a hash,
> but again, I just didn't get it to work :-(

Although it is very likely, there is no guarantee that data will come
out of a database in the same order for two separate select statements,
so you need to fetch the value of ID at the same time as the names.
That's not difficult at all - just add id (or whatever the name of your
column is) to the list of columns in the select statement, like this:

  my $sql = 'select id, firstname, secondname from supp_verantw_v';  
  my $personen = $dbh->selectall_arrayref($sql) or die "geht net: $!";

Then you can access their values like this:

  foreach my $person (@$personen) {
    my $id = $person->[0];
    my $fullname = $person->[2].' '.$person->[1];
      :
      :
  }

You can use selectall_hashref instead, when you will get a hash of
hashes instead of an array of arrays. You have to specify in the call
which of the table columns you want to act as hash keys to the records
returned. You will want to use 'id', and your code will look like this:

  my $sql = 'select id, firstname, secondname from supp_verantw_v';  
  my $personen = $dbh->selectall_hashref($sql, 'id') or die "geht net: $!";

  foreach my $id (keys %$personen) {
    my $fullname = $personen->{$id}{secondname}.' '.$personen->{$id}{firstname};
      :
      :
  }

But the disadvantage of using this method is that, because hashes are
unordered, any sorting done by the database engine (using 'order by')
would be ineffective. Because the data are appearing in an HTML list of
<option> elements you will probably want to present them in name, or
possibly ID order. That means you would have to sort the records in Perl
after fetching them from the database, which isn't nice programming, and
for that reason I recommend you stick with selectall_arrayref and add an
'order by' to the SQL.

(DBI does offer a third option which offers the best of both worlds and
returns an array of hashes. If you write

  my $sql = 'select id, firstname, secondname from supp_verantw_v order by 
secondname, firstname';  
  my $personen = $dbh->selectall_arrayref($sql, { Slice => {} }) or die "geht 
net: $!";

then you will get an (ordered) array of hashes. But I won't go any
further into that for fear of confusing you.

> Any help GREATLY appreciated - what a frustrating day today :-(

I hope this helps you to complete your task, and makes tomorrow a better
day!

- Rob

-- 
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to