Database table to keep track of objects I have a table called FoodMaker and another table called Settings. Every FoodMaker has multiple Settings. I also need a 3rd table to record what has been made, when and with what Settings. It might have these fields: id, food_made, date_time, settings_used. 

This is pretty straightforward since I can put IDs' of FoodMaker and Settings in the 3rd table to reference them. But the problem is, the Settings may change later but I want the Settings which the food has been made with and not the updated one. So I need to take a snapshot of Settings each time I'm INSERTING into 3rd table. 

One easy way could be just to add all of the Settings fields to the 3rd table on each INSERT. But is it not redundant and ugly? Is there a better solution?

I asked this question on SO but then I figured out this might be a better place to post it.
It is quite common to copy the data as it stands at the point in time that a transaction occurred - in your case the Settings when a FoodMaker made something.  This happens with financial systems a lot for audit and reporting purposes.  Yes, it is duplication, but in a known and controlled way for a purpose.

The alternative is to add time range columns to Settings.  These delimit the interval in which those settings values are applicable.  The third table must hold the datetime when a row is inserted to it, as you propose.  When you read you add the predicate

.. and Third.date_time &gt;= Settings.FromDateTime and Third.date_time &lt; Settings.ToDateTime


With this approach you have to be very careful to never update values in Settings, only ever the ToDate, or insert whole new rows.  Ensuring there are no gaps or overlaps in these time ranges can be tricky, too.  Frequent changes to Settings can cause this table to bloat, slowing down the transactional part of your application.  Eventually you will want to purge old Settings which will fragment the table.

With a separate audit table containing snapshots of values most of these concerns go away.  You can deny all UPDATE activity on it, allowing only INSERTS so there is no risk of history changing.  The table can be partitioned by date allowing for fast purging.  Indeed, the whole table can be removed to a separate data mart for long-term storage and reporting.