If an invitations is just an invitation, then it seems simplest to store all of the users for an invite in their own row in a separate table.
Users
- UserID, PK
- other details about the users
Invitations
- InvitationID, PK
- OwnerID or OwningUserID or just UserID, FK to Users
- other details about the invitations
InvitationUsers
- InvitationID, FK to Invitations
- UserID FK to Users
[PK on (InvitationID, UserID)]
A lot of people are tempted to store all of the users of an invitation in a comma-separated list or as XML. Resist this temptation; it leads to nothing but trouble.
If there is only a single type of invitation (like "be my friend on Facebook and we can hug and all that") then a simpler model might be:
Users
- UserID, PK
- other details about the users
Invites
- SenderID, FK to Users(UserID)
- RecipientID, FK to Users(UserID)
[PK on (SenderID, RecipientID)]
If an owner can send an invitation to various things (a dating service, a boat show and a movie) then we'll need more details about what those relevant things are.
Like Mike says, this is not something that you should be doing with the database. This is more of an algorithm question.
Your problem looks very similar to the knapsack problem, which is NP-Complete. Fortunately, you are not looking for the set that adds up to exactly some amount of calories, but a range, so it's not NP-Complete. Luckily for you, a few hundred recipes will easily fit in memory, so you can easily write an algorithm for it.
Here's a simple algorithm. I'm sure there's a better way to do it:
orderedRecipes = recipes sorted by calorie asc
def combinations(recipes, lowerBound, upperBound, recipeCount):
first = recipes[0]
if first.calorie < upperBound:
for c in combinations(recipes[1:], lowerBound - first.calorie, upperBound - first.calorie, recipeCount - 1):
if sum(r.calorie for r in [first] + c) > lowerBound:
yield [first] + c
for c in combinations(recipes[1:], lowerBound, upperBound, recipeCount):
if sum(r.calorie for r in c) > lowerBound:
yield c
combinationsOf3Between440and460 = list(combinations(orderedRecipes, 440, 460, 3))
The first loop takes the first item in the rest of the list, and the second loop does not. You don't want it to always take the first item, you want both take and no-take.
Best Answer
I personally would not design it so that I had to create new tables all of the time. As you already track the
user_id
in themeals
table, I would just add another column for thedate
to it and remove theday
table altogether. I would advise to not make thedate_time
attribute as the primary key though because it is highly probable that more that one person will want to eat on the same day...You also have a redundant attribute
user_user_id
in themeals
table because it is already included asuser_id
.The
user_name
field does not belong in themeals
table because it is an attribute of auser
. You can however include theuser_name
in the query used to obtainmeals
items like so: