Previous I said to do this:
Mage::getModel('catalog/product_flat_indexer')->rebuild();
Note: it's getModel and NOT getResourceModel.
This is not true. Either works. However, I have found through a rather painful trial and error process that the flat product tables don't rebuild correctly unless I also rebuild the entire catalog. This is how I finally solved my problem:
Mage::getSingleton('catalog/index')->rebuild();
Mage::getResourceModel('catalog/product_flat_indexer')->rebuild();
Mage::getSingleton('catalog/url')->refreshRewrites();
Mage::getModel('catalog/product_image')->clearCache();
Mage::getSingleton('catalogsearch/fulltext')->rebuildIndex();
Mage::getSingleton('cataloginventory/stock_status')->rebuild();
$flag = Mage::getModel('catalogindex/catalog_index_flag')->loadSelf();
if ($flag->getState() == Mage_CatalogIndex_Model_Catalog_Index_Flag::STATE_RUNNING) {
$kill = Mage::getModel('catalogindex/catalog_index_kill_flag')->loadSelf();
$kill->setFlagData($flag->getFlagData())->save();
}
$flag->setState(Mage_CatalogIndex_Model_Catalog_Index_Flag::STATE_QUEUED)->save();
Mage::getSingleton('catalogindex/indexer')->plainReindex();
Basically, just rebuild everything. Don't worry about optimization. Like someone once said, "Premature optimization is the root of all evil."