Skip to content

Commit c99a8c7

Browse files
add association tables to the foreign key chapter.
1 parent f9fc4ed commit c99a8c7

File tree

4 files changed

+110
-7
lines changed

4 files changed

+110
-7
lines changed

book/20-concepts/00-models.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Innovations in data models have spurred progress by creating new mental tools fo
2121
Scientists and engineers who become well-versed in effective data models can collaborate more efficiently because they share a common conceptual framework.
2222

2323
:::{hint} What data models do you already know?
24-
Before moving forward, take a moment to consider the different data models you're already familiar with.
24+
Before moving forward, take a moment to consider the different data models you're already familiar with. Perhaps you've worked with a spreadsheet, a database, or a programming language but didn't know that they were distinct data models.
2525
Describe a common data model by naming the building blocks that the model uses to represent and manipulate data.
2626
What are the key operations for creating, modifying, and querying data within this data model?
2727
What constraints does the model impose to reduce errors?

book/30-schema-design/020-primary-key.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ CREATE TABLE marathon_champions (
200200
division ENUM('men', 'women') NOT NULL,
201201
name VARCHAR(60) NOT NULL,
202202
country CHAR(2) NOT NULL,
203-
time_in_seconds : decimal(8,3) NOT NULL,
203+
time_in_seconds DECIMAL(8,3) NOT NULL,
204204
PRIMARY KEY (year, division)
205205
);
206206
```

book/30-schema-design/030-foreign-keys.ipynb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,73 @@
396396
"However, DataJoint does not support updating primary key values, as this can risk breaking referential integrity in complex scientific workflows. The preferred and safer pattern in DataJoint is to **delete the old record and insert a new one** with the updated information."
397397
]
398398
},
399+
{
400+
"cell_type": "markdown",
401+
"metadata": {},
402+
"source": [
403+
"## Association Tables: Many-to-Many Relationships\n",
404+
"\n",
405+
"The simple one-to-many relationship we've seen so far (Employee → Title) is just the beginning. In real-world applications, you often need to model **many-to-many relationships** where entities can be connected in complex ways.\n",
406+
"\n",
407+
"Consider a scenario where you want to track which languages each person speaks and their fluency level. A person can speak multiple languages, and each language can be spoken by multiple people. This is a classic many-to-many relationship.\n",
408+
"\n",
409+
"To model this, we create an **association table** (also called a **junction table** or **bridge table**) that serves as an intermediary between the two main entities. The association table contains foreign keys to both parent tables, and often includes additional attributes that describe the relationship itself.\n"
410+
]
411+
},
412+
{
413+
"cell_type": "code",
414+
"execution_count": null,
415+
"metadata": {},
416+
"outputs": [],
417+
"source": [
418+
"# Create a new schema for our language example\n",
419+
"import random\n",
420+
"from faker import Faker\n",
421+
"\n",
422+
"schema_lang = dj.Schema('languages')\n",
423+
"fake = Faker()\n",
424+
"\n",
425+
"@schema_lang\n",
426+
"class Language(dj.Lookup):\n",
427+
" definition = \"\"\"\n",
428+
" lang_code : char(4)\n",
429+
" ---\n",
430+
" language_name : varchar(30)\n",
431+
" \"\"\"\n",
432+
" \n",
433+
" contents = [\n",
434+
" (\"ENG\", \"English\"),\n",
435+
" (\"SPA\", \"Spanish\"), \n",
436+
" (\"JPN\", \"Japanese\"),\n",
437+
" (\"TAG\", \"Tagalog\"),\n",
438+
" (\"MAN\", \"Mandarin\"),\n",
439+
" (\"POR\", \"Portuguese\")\n",
440+
" ]\n",
441+
"\n",
442+
"@schema_lang\n",
443+
"class Person(dj.Manual):\n",
444+
" definition = \"\"\"\n",
445+
" person_id : int\n",
446+
" ---\n",
447+
" name : varchar(60)\n",
448+
" date_of_birth : date\n",
449+
" \"\"\"\n",
450+
"\n",
451+
"@schema_lang \n",
452+
"class Fluency(dj.Manual):\n",
453+
" definition = \"\"\"\n",
454+
" -> Person\n",
455+
" -> Language\n",
456+
" ---\n",
457+
" fluency_level : enum('beginner', 'intermediate', 'fluent')\n",
458+
" \"\"\"\n"
459+
]
460+
},
461+
{
462+
"cell_type": "markdown",
463+
"metadata": {},
464+
"source": []
465+
},
399466
{
400467
"cell_type": "markdown",
401468
"metadata": {},

book/50-queries/070-aggregation.ipynb

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -719,9 +719,14 @@
719719
"dj.Diagram(sales)"
720720
]
721721
},
722+
{
723+
"cell_type": "markdown",
724+
"metadata": {},
725+
"source": []
726+
},
722727
{
723728
"cell_type": "code",
724-
"execution_count": 15,
729+
"execution_count": null,
725730
"metadata": {},
726731
"outputs": [
727732
{
@@ -825,7 +830,7 @@
825830
},
826831
{
827832
"cell_type": "code",
828-
"execution_count": 25,
833+
"execution_count": null,
829834
"metadata": {},
830835
"outputs": [
831836
{
@@ -923,9 +928,40 @@
923928
}
924929
],
925930
"source": [
926-
"# SHOW ALL the employees, the number of their direct reports\n",
931+
"# Show all employees and the number of their direct reports\n",
927932
"\n",
928-
"Employee.proj(reports_to='employee_number').aggr(Report, n='count(employee_number)')"
933+
"```{tab-set}\n",
934+
"```{tab-item} DataJoint\n",
935+
"```python\n",
936+
"Employee.proj(reports_to='employee_number').aggr(Report, n='count(employee_number)')\n",
937+
"```\n",
938+
"```{tab-item} SQL\n",
939+
"```sql\n",
940+
"SELECT employee.employee_number, first_name, last_name, count(report.employee_number) as n \n",
941+
"FROM employee LEFT JOIN report ON (employee.employee_number = report.reports_to)\n",
942+
"GROUP BY employee.employee_number\n",
943+
"```\n",
944+
"```"
945+
]
946+
},
947+
{
948+
"cell_type": "markdown",
949+
"metadata": {},
950+
"source": [
951+
"# Show all employees and the number of their direct reports\n",
952+
"\n",
953+
"```{tab-set}\n",
954+
"```{tab-item} DataJoint\n",
955+
"```python\n",
956+
"Employee.proj(reports_to='employee_number').aggr(Report, n='count(employee_number)')\n",
957+
"```\n",
958+
"```{tab-item} SQL\n",
959+
"```sql\n",
960+
"SELECT employee.employee_number, first_name, last_name, count(report.employee_number) as n \n",
961+
"FROM employee LEFT JOIN report ON (employee.employee_number = report.reports_to)\n",
962+
"GROUP BY employee.employee_number\n",
963+
"```\n",
964+
"```\n"
929965
]
930966
},
931967
{
@@ -1143,7 +1179,7 @@
11431179
},
11441180
{
11451181
"cell_type": "code",
1146-
"execution_count": 50,
1182+
"execution_count": null,
11471183
"metadata": {
11481184
"vscode": {
11491185
"languageId": "sql"

0 commit comments

Comments
 (0)