Il problema era che la query tirava fuori due colonne "category_id" e al momento della stampa prendeva il category_id della tabella sub_categories anziché quella di categories.
Ho risolto con un alias.
Il problema era molto banale, sigh.
$sql = "select *, c.category_id AS ccategory_id from categories c left join sub_categories s on c.category_id = s.category_id order by c.category_id";
$result = mysql_query($sql);
while ($row = mysql_fetch_array($result)) {
print("<tr><td bgcolor=\"#003399\">[b]");
printf("
ID categoria: %s\n", $row["ccategory_id"]);
printf("
Nome categoria: %s\n", $row["category_name"]);
printf("
%s<hr>\n", $row["category_desc"]);
if (isset($row["sub_category_id"])):
printf("
ID sottocategoria: %s\n", $row["sub_category_id"]);
printf("
Nome sottocategoria: %s\n", $row["sub_category_name"]);
printf("
%s</td></tr>\n", $row["sub_category_desc"]);
endif;
}