database structure for multiple ingredients - mysql

Below I have a basic database for recipes. I want to have a table of ingredients that are static, and each time a user adds an ingredient 3 ids gets put into the recipe_ingredient table, 1 for the new recipe_ingredient, 1 for the recipe its for and 1 for the ingredient. I want to know if this is practical or what would be a good way to store ingredients for recipes.
---RECIPES--- ---INGREDIENTS---
+-> ID: ID: <-+
| NAME: NAME: |
| PHOTO: |
| |
| ---RECIPE_INGREDIENT--- |
| ID: |
+------+ RECIPE_ID: |
INGREDIENT_ID: +--------+
AMMOUNT:

Related

Need help building an odd Sequelize query

Some context:
I have two tables: challenges and participants.
challenges table columns: id, userId, and name.
participants table columns: id, userId, and challengeId
The userId column on the challenge table indicates ownership of a challenge.
The participants table is simply an association table to keep track of users who have joined challenges.
challenges.hasMany(participants)
participants.belongsTo(challenges)
The owner of a challenge may or may not be a participant.
My question:
How can I construct a single Sequelize query to find all owned OR joined challenges for a specific user?
Example Data:
challenges
+----+--------+-------------+
| id | userId | name |
+----+--------+-------------+
| 1 | 2001 | Challenge 1 |
| 2 | 2002 | Challenge 2 |
+----+--------+-------------+
participants
+----+--------+-------------+
| id | userId | challengeId |
+----+--------+-------------+
| 1 | 2001 | 1 |
| 2 | 2002 | 1 |
+----+--------+-------------+
Desired result of finding all owned OR joined challenges of user 2002:
challenges: [
{
id: 1,
userId: 2001,
name: "Challenge 1"
},
{
id: 2,
userId: 2002,
name: "Challenge 2"
}
]
Give this a try:
SELECT challenges.*
FROM challenges
LEFT JOIN participants
ON challenges.id = participants.challengeId
WHERE challenges.userId = 2002
OR participants.userId = 2002;

Sequelize - Matching all records of a given entity in mySQL database

I have three MySQL (v8) tables
TABLE 1:
students (contains details of all students)
- id
- full_name
- email
Records:
| id | full_name | email |
|----|-----------|-------------------|
| 1 | John | john#example.com |
| 2 | Adam | adam#example.com |
| 3 | James | james#example.com |
| 4 | Jane | jane#example.com |
TABLE 2:
courses (contains all courses)
- id
- title
Records:
| id | title |
|----|--------|
| 1 | PHP |
| 2 | Python |
| 3 | .Net |
| 4 | HTML |
TABLE 3:
student_courses (this table contains which student has subscribed to what courses)
- student_id
- course_id
Records:
| id | student_id | course_id |
|----|------------|-----------|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 1 |
The problem I am facing here is I need to get a list of all students who have opted for both course ids 1 & 2, which in the above example is "John".
Using sequelize I have tried the following two where clauses, but both giving me incorrect results.
Option 1) This is giving me empty result set
where: {
course_id: {
[Op.and]: [1,2]
}
}
Option 2) This is returning "John" as well as "James". It shouldn't return "James" since he has subscribed to only course id 1.
where: {
course_id: [1, 2]
}
What am I missing here?
Thanks in advance
You can achieve N:M associations by using this, More information can be found here http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-belongsToMany
//add required associations
students.associate = (models) => {
students.belongsToMany(models.courses, {
through: 'student_courses',
foreignKey: 'student_id'
});
};
// now query the db like this
db.students.findAll({
where: { full_name : 'john'},
include: [{
model: db.courses,
where: {
id: {
[Op.and]: [1,2]
}
}
}]
})

Link to multiple foreign rows from one row

I really don't know how this is called so I couldn't find an answer.
For example I have the following tables:
Table products Table users
+----------+----------+----------+ +----------+----------+
| id | name | price | | username | products |
+----------+----------+----------+ +----------+----------+
| 1 | Bread | 1.5 | | James | 1, 2 |
+----------+----------+----------+ +----------+----------+
| 2 | Cookies | 2.0 | | David | 2, 3 |
+----------+----------+----------+ +----------+----------+
| 3 | Milk | 1.2 | | Paul | 3, 1 |
+----------+----------+----------+ +----------+----------+
products.id is the foreign key for users.products.
I would like to get the name and price of all David's products using a MySQL query. This should return the following:
+----------+----------+
| name | price |
+----------+----------+
| Cookies | 2.0 |
+----------+----------+
| Milk | 1.2 |
+----------+----------+
The JOIN function would be the best to use I guess, but David can have just 1 or 100 products. Creating 100 columns to fit all products doesn't sound very efficient.
How can I realize this?
Currently I'm just fetching everything and filter it using PHP, this is not a problem at the moment but as the tables will grow I guess this will be very inefficient.
This could be solved by changing your data model.
users
+----------+----------+
| id | username |
+----------+----------+
| 1 | Fred |
+----------+----------+
| 2 | John |
+----------+----------+
products
+----------+----------+----------+
| id | name | price |
+----------+----------+----------+
| 1 | Bread | 1.5 |
+----------+----------+----------+
| 2 | Cookies | 2.0 |
+----------+----------+----------+
| 3 | Milk | 1.2 |
+----------+----------+----------+
And here comes the magic: You could connect the two tables using a third table:
user_procuct_connections
+----------+----------+------------+
| id | user_id | product_id |
+----------+----------+------------+
| 1 | 1 | 2 | -> Fred has Cookies
+----------+----------+------------+
| 2 | 1 | 3 | -> Fred also has Milk
+----------+----------+------------+
| 3 | 2 | 1 | -> John has Bread
+----------+----------+------------+
If you want a user to be able to own a single product only, then you can remove the id column, an make the user_id and product_id primary key together.
Then when you want to get for example all of Freds products then just
SELECT
*
FROM
products
WHERE
id IN (
SELECT
product_id
FROM
user_procuct_connections
WHERE
user_id = 1
)
You could try this:
SELECT * FROM products pt
where FIND_IN_SET(pt.id,(select us.prices from users us
WHERE us.username = "David"));
Working fiddle: http://sqlfiddle.com/#!2/4f78d/2
The design for the 'Users' table is wrong. Instead of working around this bad design, please change the design.
So effectively, you design could be as such:
Table Products :
ID Name Price
1 Bread 1.5
2 Cookies 2.0
3 Milk 1.2
Table users :
Username Products
James 1
James 2
David 2
David 3
Paul 3
Paul 1
You see, you can have multiple rows for each user i.e.one row for each product in the Users table. You can maintain another Boolean field to identify which ones are currently 'active' or 'applicable'. Joins' would be much easier in that case. Also, updating records would also be easier. In case, in future you'd want to retrieve & analyse data from historic records, that would be possible too!!!
Another thing with the current design is that times and again you'd have to work around the infamous "comma-seperated values". Let's say you have a record as such:
Username Products
James 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
If you need to sort this data into rows, you'd have to work-around the 'Comma-seperated' values so many times. Imagine doing that for 'n' number of records!!
Hope this helps!!!
If you join the tables like:
SELECT name, price FROM products AS p INNER JOIN user_products AS up ON up.id = p.product_id AND up.user_id = <davids id>
You will get an array where first column will be the name and the second the price. You can then do what you want with it in PHP and change the array. You don't have to filter anymore.

Application specific MySQL table Structure

I have a question about my DB table structure. I want to know if i'm on the right track or if I have missed a good alternative. Here is the case:
To make it easy to read, I haven't pasted the full contents as my question is only about the structure.
2 tables:
1: id (AI), task
2: id, name, task
Table 1 presents dynamic check-boxes which can be altered by an admin panel so the contents would be like this
1 task1
2 task2
5 task5
(3 & 4 are missing cause the administrator deleted those records).
In table number two are the people who should do the tasks from table 1. And the goal is that the tasks wich are not checked will be displayed.
So the contents of table 2 would be:
1 Name1 1,5
2 Name2 1,2
3 Name3 1,2,5
The numbers in table 2 represent the checked boxes from table 1. So with a query i can compare the numbers from table 2 with the id's from table 1 and display the missing ids as "todo".
In my opinion this looks very overdone, and there must be an easier way to create dynamic options which can be compared and stored as a todo.
Suggestions are welcome!
I suggest you to use basic structure for many-to-many relationship:
tasks users user_tasks
+----+-----------+ +----+-------+ +---------+---------+
| id | name | | id | name | | user_id | task_id |
+----+-----------+ +----+-------+ +---------+---------+
| 1 | Buy milk | | 1 | John | | 1 | 2 |
| 2 | Get drunk | | 2 | Tim | | 3 | 2 |
| 3 | Have fun | | 3 | Steve | | 2 | 4 |
| 4 | Go home | +----+-------+ | 3 | 4 |
+----+-----------+ +---------+---------+
And you can fetch unassigned tasks using following query:
SELECT
tasks.*
FROM
tasks
LEFT JOIN
user_tasks
ON (tasks.id = user_tasks.task_id)
WHERE
user_tasks.user_id IS NULL
You also can fetch users who have no assigned tasks:
SELECT
users.*
FROM
users
LEFT JOIN
user_tasks
ON (users.id = user_tasks.user_id)
WHERE
user_tasks.user_id IS NULL
Hope this will help you.

can couchdb do loops

Can couchdb do loops?
Let's say I have a database of interests that have 3 fields
subject1,subject2,subject3. example, cats,nutrition,hair or space,telescopes,optics etc.
A person (A) has 10 interests composed of 3 fields each.
10 more people B,C,D...have 10 interests each composed of 3 subjects each.
When person A logs in I want the system to search for all people with matching interests.
In javascript I would normally loop through all the interests and then find matching ones I guess using
two loops. Then store the matches in another database for the user like "matchinginterests".
Is there any easy way to do this in couchdb compared to mysql -- which seems very complicated.
Thanks,
Dan
I think I understand what you are asking. The answer is pretty straightforward with Map/Reduce.
Say you have the following people documents:
{
"name": "Person A",
"interests" [ "computers", "fishing", "sports" ]
}
{
"name": "Person B",
"interests" [ "computers", "gaming" ]
}
{
"name": "Person C",
"interests" [ "hiking", "sports" ]
}
{
"name": "Person D",
"interests" [ "gaming" ]
}
You would probably want to emit your key as the interest, with the value as the person's name (or _id).
function (doc) {
for (var x = 0, len = doc.interests.length; x < len; x++) {
emit(doc.interests[x], doc..name);
}
}
Your view results would look like this:
computers => Person A
computers => Person B
fishing => Person A
gaming => Person B
gaming => Person D
hiking => Person C
sports => Person A
sports => Person C
To get a list of people with computers as an interest, you can simply send key="computers" as part of the query string.
If you want to add a reduce function to your map, you can simply use _count (shortcut to use a compiled reduce function) and you can retrieve a count of all the people with a particular interest, you can even use that to limit which interests you query to build your relationships.
When person A logs in I want the system to search for all people with matching interests.
SELECT i_them.* FROM interests AS i_me
INNER JOIN interests AS i_them ON (i_them.person != i_me.person) AND
((i_them.subject1 IN (i_me.subject1, i_me.subject2, i_me.subject3)) OR
(i_them.subject2 IN (i_me.subject1, i_me.subject2, i_me.subject3)) OR
(i_them.subject3 IN (i_me.subject1, i_me.subject2, i_me.subject3)))
WHERE i_me.person = 'A'
Is that what you wanted to do?
If you design your tables a little smarter though you'd do it like
SELECT DISTINCT them.* FROM person AS me
INNER JOIN interest AS i_me ON (i_me.person_id = me.id)
INNER JOIN interest AS i_them ON (i_them.subject = i_me.subject)
INNER JOIN person AS them ON (them.id = i_them.person.id AND them.id != me.id)
WHERE me.name = 'A'
Using the following tables
table interest
id integer primary key autoincrement
person_id integer //links to person table
subject varchar //one subject per row.
+-----+-----------+---------+
| id | person_id | subject |
+-----+-----------+---------+
| 1 | 3 | cat |
| 2 | 3 | stars |
| 3 | 3 | eminem |
| 4 | 1 | cat |
| 5 | 1 | dog |
| 6 | 2 | dog |
| 7 | 2 | cat |
table person
id integer primary key autoincrement
name varchar
address varchar
+-----+------+---------+
| id | name | address |
+-----+------+---------+
| 1 | A | here |
| 2 | Bill | there |
| 3 | Bob | everyw |
result
+-----+------+---------+
| id | name | address |
+-----+------+---------+
| 2 | Bill | there |
| 3 | Bob | everyw |
This is how (what you call) 'looping' in SQL works...
First you take person with name 'A' from the table.
me.id me.name me.address
| 1 | A | here |
You look up all the interests
me.id me.name me.address i_me.subject
| 1 | A | here | cat
| 1 | A | here | dog
Then you match everyone elses interests
me.id me.name me.address i_me.subject i_them.subject i_them.person_id
| 1 | A | here | cat | cat | 3
| 1 | A | here | cat | cat | 2
| 1 | A | here | dog | dog | 2
And then you match the person to them's interest (except for me of course)
me.id me.name me.address i_me.subject i_them.subject i_them.person_id them.name
| 1 | A | here | cat | cat | 3 | Bob
| 1 | A | here | cat | cat | 2 | Bill
| 1 | A | here | dog | dog | 2 | Bill
Then you return only the data from them and 'throw' the rest away, and remove duplicate rows DISTINCT.
Hope this helps.