There are other ways to do this but this way may be easy to follow. The difficulty arises because of the ordering of the cities within the columns.
<?PHP
/*
Given the following:
CREATE TABLE mytable (
state varchar(30) NOT NULL default '',
city varchar(30) NOT NULL default ''
) TYPE=MyISAM;
INSERT INTO mytable VALUES ('New Jersey', 'Bloomfield');
INSERT INTO mytable VALUES ('New Jersey', 'Caldwell');
INSERT INTO mytable VALUES ('New Jersey', 'Denville');
INSERT INTO mytable VALUES ('New Jersey', 'Newark');
INSERT INTO mytable VALUES ('New Jersey', 'New Brunswick');
INSERT INTO mytable VALUES ('Nevada', 'Las Vegas');
INSERT INTO mytable VALUES ('Nevada', 'Reno');
*/
mysql_connect("host","userid","password");
mysql_select_db("database");
// Start table.
echo "<table border=0 cellpadding=2>\n";
// Go through state one at a time.
$result1=mysql_query("SELECT DISTINCT state FROM mytable ORDER BY state");
while ($row1=mysql_fetch_assoc($result1)) {
$state=$row1['state'];
// Get cities for this state and put in an array.
$result2=mysql_query("SELECT city FROM mytable WHERE state='$state'");
while ($row2=mysql_fetch_assoc($result2)) $cities[]=$row2['city'];
// Calculate number of cities in each column.
$incol=min(count($cities), 50); // << change to a number other than 50 if desired.
// Remember index for first city in second column.
$c2=$incol;
// Generate each row of state data.
for ($c1=0; $c1<$incol; $c1++, $c2++) {
// Begin row.
echo "<tr>";
// Display state cell.
if ($c1==0) echo "<td>$state</td>"; else echo "<td> </td>";
// Display value for first city column.
echo "<td>" . $cities[$c1] . "</td>";
// Display value for second city column.
if ($c2<count($cities)) echo "<td>" . $cities[$c2] . "</td>"; else echo "<td> </td>";
// End row.
echo "</tr>\n";
}
// Clear cities table.
unset($cities);
}
// End table.
echo "</table>\n";
?>