too long

I want to be able to display a table with a list of people in one column and a string of their orders in another.

People
========
person_id
fname
lname

Orders
========
order_id
person_id
product_id

Products
=========
product_id
productname

I have the following code:

SELECT people.fname, people.lname, products.productname
FROM people
INNER JOIN orders ON people.person_id = orders.person_id
INNER JOIN products ON products.product_id = orders.product_id
ORDER BY lname
...
$rows = $result->num_rows;
while($row = $result->fetch_assoc()) {
    echo "<tr><td>Edit</td>
    <td>".$row["lname"].", ".$row['fname']."</td>
    <td>".$row['productname']."</td></tr>";
    }

This displays:
Smith, Bob | Item 1
Smith, Bob | Item 2
Smith, Bob | Item 3
Roberts, Jill | Item 2
etc..

What I want is for it to display:
Smith, Bob | Item 1, Item 2, Item 3
Roberts, Jill | Item 2

How do I show the name (based on person_id) only once and then a list of their orders in another column?

In MySQL, you have to use the GROUP_CONCAT function. For example something like this:

SELECT CONCAT(LNAME,", ",FNAME), GROUP_CONCAT(PRODUCTNAME)
FROM PEOPLE P, PRODUCTS PR, ORDERS O
WHERE P.PERSON_ID = O.PERSON_ID
AND O.PRODUCT_ID = PR.PRODUCT_ID
GROUP BY (LNAME, FNAME);

Php oriented solution :

You could store your product names in an array, using the person's name as a key :

$rows = $result->num_rows;

$data = array();
while($row = $result->fetch_assoc())
{
     $name = $row["lname"].", ".$row['fname'];
     $data[$name][] = $row['productname'];
}

Then, you'll just have to loop through this array :

foreach($data as $name => $products)
{
     echo "<tr><td>Edit</td><td>".$name."</td>";
     foreach($products as $productname)
     {
         echo "<td>".$productname."</td>";
     }
     echo "<tr>";
}