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

Hello Monks - I have two tables I would like to join: catalog_pet and colors. The catalog_pet tables has item_id as a unique field and the colors table has item_id and color fields. I have item_id's that have multiple colors: for example: item_id 7 has blue, green, and orange. I would like to create a popup menu that shows Item #7 with the three choice of colors. The result that I'm getting is three instances of Item 7 each with the different color. For example, Item 7 with choice of blue. Then, Item 7 with choice of green. Then, Item 7 with choice of orange. I shouldn't have Item 7 appearing three times. It should appear once with the ability to pick a color. I've pasted the necessary code used to create all this. I hope someone can give me some help..... The first section of code gathers the items within each category:
sub get_category_items { my @placeholder; my ($cat_name, $where, $limit, $value); $where = qq( WHERE catalog_pet.item_id = colors.item_id AND category = + ? ); $value = param("cat"); if ($value ne "") { push (@placeholder, $value); } shop_parameters($dbh, $cat_name, $where, @placeholder); }
Everything is passed to the next subroutine....
sub shop_parameters { my ($dbh, $cat_name, $where, @placeholder) = @_; my ($start_pos, $page_size, $max_rec); my @nav_link; # navigation link array my $limit; # LIMIT clause my @row; # summary table row array my ($stmt, $sth); my $str; my $page; # Get the page control parameters. If they're not present, this i +s # the first time we're running this search. In that case, run a q +uery # to determine the result set size and initialize the page paramet +ers. #@ INIT_PAGE_PARAMS $start_pos = param ("start_pos"); $page_size = param ("page_size"); $max_rec = param ("max_rec"); if (!defined (param ("start_pos"))) { $start_pos = 0; $page_size = 2; # change this to change #hits/page $stmt = "SELECT COUNT(*) FROM catalog_pet, colors $where"; $max_rec = $dbh->selectrow_array ($stmt, undef, @placeholder); if ($max_rec == 0) { print p ("Sorry, no qualifying listings were found."); return; } # put values into environment so nav_gen_link() can find them # (except for start_pos, which isn't constant across links) param (-name => "page_size", -value => $page_size); param (-name => "max_rec", -value => $max_rec); } #@ INIT_PAGE_PARAMS # $start_pos = number of initial records to skip # $page_size = number of records to retrieve $limit = "LIMIT $start_pos, $page_size"; print p ("$max_rec matching listings were found."); $sth = $dbh->prepare ("SELECT * FROM catalog_pet, colors $where ORDER +BY description $limit"); $sth->execute (@placeholder); $page = get_product_table ($sth); if ($page) { $page = p ({-style=>"font-family: verdana; font-size: 10pt;"}, + "Items in product category:\n" . escapeHTML (@placeholder)) . $page; } else { # if the category is empty, say so and show the category list +again $page .= p ({-style=>"font-family: verdana; font-size: 10pt;"} +, "There are no items in this product category:\n" . escapeHTML (@placeholder)); $page .= get_category_list ($dbh); } #@ BUILD_NAV_LINKS if ($max_rec > $page_size) { #@ PREV_PAGE_LINK if ($start_pos == 0) # first page: no prede +cessor { push (@nav_link, "previous"); } else { push (@nav_link, nav_gen_link ("previous", $start_pos-$pag +e_size)); } #@ PREV_PAGE_LINK for (my $i = 0; $i < $max_rec; $i += $page_size) { my $page_no = int ($i / $page_size) + 1; if ($start_pos == $i) # this is the current + page { push (@nav_link, $page_no); } else { push (@nav_link, nav_gen_link ($page_no, $i)); } } if ($start_pos+$page_size >= $max_rec) # last page: no succ +essor { push (@nav_link, "next"); } else { push (@nav_link, nav_gen_link ("next", $start_pos+$page_si +ze)); } @nav_link = map { "[$_]\n" } @nav_link; $sth->finish (); $page .= "@nav_link"; } #@ BUILD_NAV_LINKS return ($page); }
Finally, this code produces the result....
sub get_product_table { my $sth = shift; my @row; my @nav_link; while (my $ref = $sth->fetchrow_hashref ()) { my $serve_url = sprintf ("serve_image.cgi?item_id=%s;picture", es +cape ($ref->{item_id})); # generate a form allowing a quantity of the item to be added # to the cart push (@row, start_form(-method=>'POST', -action=>url()), hidden( -name => "choice", -override => 1, -default => "add" ), hidden( -name => "item_id", -override => 1, -default => escapeHTML($ref->{item_id})), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td (img ({-src => $serve_url, -alt => escapeHTML($ref->{item_id})}) +), td ({-width=>"80", -valign=>"top", -align => "center"}, escapeHTML( + $ref->{item_id} )), td ({-width=>"190", -valign=>"top"}, escapeHTML( $ref->{description +} ))), td (" "), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td ({-colspan => "3", -align => "right", -style=>"font-weight: 700; + color:blue;"}, "Price: \$", escapeHTML( sprintf( "%.2f", $ref->{pri +ce}))), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td ({-colspan => "3", -align => "right"}, "Colors:", popup_menu( -n +ame => "color", -values => $ref->{color}))), Tr ({-style=>"font-family: verdana; font-size: 10pt;"}, td ({-colspan => "3", -align => "right"}, "Quantity:", popup_menu( +-name => "quantity", -values => ["1", "2", "3", "4", "5", "6", "7", " +8", "9"])), td (image_button( -name => "Add Item", -src=>"../images/add.jpg", - +border=>"0" )))), Tr ({-valign=>"top"}, td ({-colspan=>"7", -width=>"100%"}, hr)), end_form() ); } $sth->finish (); return undef unless @row; # no items? return (table ({-border => 0, -align => "center", -style=>"font-fa +mily: verdana; font-size: 10pt;"}, @row)); }
Thanks for any help.....

update (broquaint): added <readmore> tag

Replies are listed 'Best First'.
Re: Joining Tables Problem
by cfreak (Chaplain) on Feb 17, 2003 at 19:32 UTC

    Oh my that's a lot of code. If I understand your question correctly then you've really out-done yourself. All the popular databases I'm aware of have JOIN statements (that link is the mysql version, but I'm certain the same principles apply for most databases)

    The moral: when trying to manipulate database stuff, check your databases docs first! It will save you lots of time (yes I've learned that the hard-way)

    As an extra here's a start on what you are trying to do. This *should* (untested) get all of your ID's and their coorisponding colors, one entry per color, so you'll have multple IDs that are the same (hint you can use that to put together lists to build your pop-up with). Anyway here's the query:

    SELECT item_id, color FROM catalog_pet LEFT JOIN colors USING (item_id);

    Hope that helps
    Chris

    Lobster Aliens Are attacking the world!
      Hi, Thanks for your input. I tried using your LEFT JOIN query and I'm still receiving the same results. If the item has two colors, it appears twice, each with a different color.

        That's how joins work.

        You can solve this in two different ways. First, select only the items. Run a query for each item to fetch the available colors. The other option is to continue with your single query, making sure the results are returned and sorted on the item id. Push the available colors onto an array for each item id -- you'll end up with a hash of arrays of colors.

        I'd go for the second solution.

        Yes, you're outputting a new line for every line of the return from the Database query. What you probably want to do is one of two things:

        • Go over $ref once you have it and copy it into a different hash, but instead of just saving the colour to $ref->{'color'}, make $ref->{'color'} an arrayref, then you can push new colours onto it every time you iterate over a row with the same item_id.
        • Omit colour information from your initial query. Then, for each iteration over $ref, run another query like 'SELECT * FROM colors WHERE colors.item_id = '.$ref->{'item_id'} and push the results onto $ref->{'color'} as an arrayref.

        Whichever of these two you use, you can then pass $ref->{'color'} to CGI like:

        popup_menu( -name => "color", -values => $ref->{color})

        Note that I haven't used CGI's drawing functions much, so double-check the syntax of popup_menu() before testing this out.

        Update: changed stuff to the american (mis)spelling of colour.

        LAI
        :eof

        chromatic and LAI already explained what you're doing and what the possibilities are.
        I started this node, and posted it by accident , while experimenting with a temporary table, to find out that this wouldn't lower the data transfer between your CGI and the DB (au contraire ;))

        So this is basically a useless node :(

        regards,
        tomte


Re: Joining Tables Problem
by dws (Chancellor) on Feb 17, 2003 at 19:23 UTC
    The result that I'm getting is three instances of Item 7 each with the different color.

    You need to either narrow the query or filter the results.

    SELECT * FROM ...
    is giving what you ask for, which is more than what you want.
    SELECT DISTINCT item_id, color FROM ...
    might work better. Try the two queries from a command-line query tool to see the difference.