pickledegg has asked for the wisdom of the Perl Monks concerning the following question:

I'm new to perl, and I've been stuck on this for a whole morning.
I have a hash and the keys are page names.
I have a for each loop that queries a database for each page in my hash. The query does a count() to get the number of occurences of this page.

I want to create another hash using the page names as the keys and the count() result as the values.

I'm almost there as this is the last part of my project, and its causing frustration now as I've spent a large part of the total time stuck on this stupid little bit.

At the moment its wrong as it puts the same value in all of the hash values, or something :)
Heres my code:
foreach $page(keys %yearly_page_hits){ $dbh = DBI->connect("DBI:mysql:$sqlDatabaseName:","$mysqlUsername"," +$mysqlPassword") or die "Connection Error: $DBI::errstr\n"; $sql = "select count(page) from counter_monthly where page='$page'"; $cursor = $dbh->prepare($sql); $cursor->execute or die "SQL Error: $DBI::errstr\n"; while (@row = $cursor->fetchrow_array) { print "while loop the row for $page is @row<br><br>\n"; $new_monthly_page_hits{$page}=@row; print "whileloop".$new_monthly_page_hits{$page}."<br><br>"; #push(@yearly_pages,@row); #sort(@yearly_pages); } }

Can anyone see what I'm doing wrong?
Thank you very much.

Replies are listed 'Best First'.
Re: Hashing up hashes
by Corion (Patriarch) on May 12, 2006 at 12:12 UTC

    Instead of

    $new_monthly_page_hits{$page}=@row;

    I think you want to do

    $new_monthly_page_hits{$page} = $row[0];

    to actually store the number you got back.

    But why do so much work in Perl when you can have the DB do the work?

    select page,count(page) from counter_monthly group by page

    will return you a list of pairs (page, hitcount). If you only want a very small subset of the whole database as not all pages are of interest to you, then your approach might be faster though.

Re: Hashing up hashes
by ptum (Priest) on May 12, 2006 at 13:40 UTC

    You've already received some good advice about the question you originally asked, but here are a few more pointers:

    • Inside your foreach loop you are creating a new database handle. This is very inefficient and should be avoided. You want to create a database handle once for an average script and reuse it, rather than paying the connection overhead multiple times. Also, some databases have rather low limits on how many connections can be opened concurrently.
    • You received a recommendation about having SQL do the work -- I would definitely take that advice. In cases where you need to step through a list of values, reusing those values in a query, you'll want to read the DBI documentation until you understand how to prepare a statement handle and use the '?' placeholders (to avoid the overhead of recompiling your SQL statement a large number of times).
    • I suspect you are still not writing 'use strict;' and 'use warnings;' at the top of your scripts. Whenever you post a code sample, you'll do well to show that you are doing this, if only to avoid people like me saying, "You really should 'use strict' and 'use warnings'! :)

    Good luck!


    No good deed goes unpunished. -- (attributed to) Oscar Wilde
      It's times like this that I wish I could ++ a post more than once.

      ---
      It's all fine and dandy until someone has to look at the code.
Re: Hashing up hashes
by liverpole (Monsignor) on May 12, 2006 at 12:14 UTC
    Hi pickledegg,

    A couple of quick observations:

    1. In the line print "while loop the row for $page is @row\n";, you should escape @row (ie. \@row), since it will otherwise try to interpolate the values of @row into the string.
    2. In the next line $new_monthly_page_hits{$page}=@row; you are assigning to the array @row in scalar context, which means you're taking the number of items in @row, NOT the actual values from the array.  Since the value you actually want to assign to is probably the first element in the array, try assigning to $row[0] instead (or use Data::Dumper to print out the contents of @row, so you can see what's going on).

    I hope that provides you some help.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Hashing up hashes
by blazar (Canon) on May 12, 2006 at 12:15 UTC
    I have a hash and the keys are page names.

    (snip)

    I want to create another hash using the page names as the keys and the count() result as the values.
    my %orig= # ... my %new; $new{$_}=however_you_get_it_from($_) for keys %orig;

    or

    my %new=map { $_ => however_you_get_it_from($_) } keys %orig;

    or...

Re: Hashing up hashes
by ruzam (Curate) on May 12, 2006 at 15:08 UTC
    That's alot of work just to get a count.
    # %yearly_page_hits defined elsewhere my %new_monthly_page_hits; # do this outside of the loop my $dbh = DBI->connect("DBI:mysql:$sqlDatabaseName:","$mysqlUsername", +"$mysqlPassword") or die "Connection Error: $DBI::errstr\n"; # loop through page keys foreach my $page (keys %yearly_page_hits) { # a single DBI call does it all if (my @row = $dbh->selectrow_array( "select count(page) from counter_monthly where page='$page'")) + { $new_monthly_page_hits{$page} = $row[0]; } else { $new_monthly_page_hits{$page} = 0; } print "$page $new_monthly_page_hits{$page}<br><br>"; }