|
4 | 4 | "cell_type": "markdown", |
5 | 5 | "metadata": {}, |
6 | 6 | "source": [ |
7 | | - "# Database Normalization \n", |
| 7 | + "# Database Normalization\n", |
8 | 8 | "\n", |
9 | | - "# Normalization Principle\n", |
| 9 | + "**Database normalization** is a set of principles for designing databases with clarity and logical rigor. Normalized designs communicate the mapping between real-world entities and their representations in database design.\n", |
10 | 10 | "\n", |
11 | | - "A fundamental principle in database design is **normalization**—the practice of organizing data to minimize redundancy and dependency. One key aspect of normalization is that **each table should represent one distinct entity class**.\n", |
| 11 | + "## Core Principle\n", |
12 | 12 | "\n", |
13 | | - "## Why Separate Entity Types?\n", |
| 13 | + "The fundamental principle of normalization is that **each table should represent one distinct entity class**.\n", |
| 14 | + "\n", |
| 15 | + "```{note}\n", |
| 16 | + "In a normalized design, each row of a given table describes a distinct entity, and no two rows in that table represent different types of entities.\n", |
| 17 | + "```\n", |
| 18 | + "\n", |
| 19 | + "## Why Normalization Matters\n", |
14 | 20 | "\n", |
15 | 21 | "Different entity types have different:\n", |
16 | 22 | "- **Identification systems**: How they are uniquely identified\n", |
17 | | - "- **Attributes**: What properties they have\n", |
| 23 | + "- **Attributes**: What properties they have \n", |
18 | 24 | "- **Relationships**: How they connect to other entities\n", |
19 | 25 | "- **Business rules**: What constraints apply to them\n", |
20 | 26 | "\n", |
21 | | - "## Example: Pet Shop Database\n", |
22 | | - "\n", |
23 | | - "Consider designing a database for a pet shop. You might be tempted to put everything in one table:\n", |
24 | | - "\n", |
25 | | - "```sql\n", |
26 | | - "-- BAD DESIGN: Mixing different entity types\n", |
27 | | - "CREATE TABLE pet_shop_data (\n", |
28 | | - " id INT PRIMARY KEY,\n", |
29 | | - " name VARCHAR(50),\n", |
30 | | - " type VARCHAR(20), -- 'cat', 'dog', 'employee', 'customer'\n", |
31 | | - " breed VARCHAR(30),\n", |
32 | | - " salary DECIMAL(10,2),\n", |
33 | | - " phone VARCHAR(20),\n", |
34 | | - " address TEXT\n", |
35 | | - ");\n", |
36 | | - "```\n", |
37 | | - "\n", |
38 | | - "This design violates the normalization principle because it mixes:\n", |
39 | | - "- **Pets** (cats, dogs) with attributes like breed\n", |
40 | | - "- **Employees** with attributes like salary\n", |
41 | | - "- **Customers** with attributes like phone and address\n", |
42 | | - "\n", |
43 | | - "## Better Design: Separate Tables\n", |
44 | | - "\n", |
45 | | - "```sql\n", |
46 | | - "-- GOOD DESIGN: Separate entity types\n", |
47 | | - "CREATE TABLE pet (\n", |
48 | | - " pet_id INT PRIMARY KEY,\n", |
49 | | - " name VARCHAR(50) NOT NULL,\n", |
50 | | - " species ENUM('cat', 'dog', 'bird', 'fish') NOT NULL,\n", |
51 | | - " breed VARCHAR(30),\n", |
52 | | - " birth_date DATE,\n", |
53 | | - " owner_id INT\n", |
54 | | - ");\n", |
55 | | - "\n", |
56 | | - "CREATE TABLE employee (\n", |
57 | | - " employee_id INT PRIMARY KEY,\n", |
58 | | - " name VARCHAR(50) NOT NULL,\n", |
59 | | - " position VARCHAR(30) NOT NULL,\n", |
60 | | - " salary DECIMAL(10,2) NOT NULL,\n", |
61 | | - " hire_date DATE NOT NULL\n", |
62 | | - ");\n", |
63 | | - "\n", |
64 | | - "CREATE TABLE customer (\n", |
65 | | - " customer_id INT PRIMARY KEY,\n", |
66 | | - " name VARCHAR(50) NOT NULL,\n", |
67 | | - " phone VARCHAR(20),\n", |
68 | | - " email VARCHAR(100),\n", |
69 | | - " address TEXT\n", |
70 | | - ");\n", |
71 | | - "```\n", |
72 | | - "\n", |
73 | | - "Each table now represents a distinct entity class with appropriate attributes and identification systems.\n", |
74 | | - "\n", |
75 | | - "**Database normalization** is a set of principles for designing databases with clarity and logical rigor. \n", |
76 | | - "Normalized designs communicate the mapping between real-world entities and their representations in database design. \n", |
77 | | - "\n", |
78 | | - "The term database normalization derives from relational database theory: \n", |
79 | | - "It applies to a data model where all data are represented as collections of related tables. \n", |
80 | | - "It may not apply equally to other data models.\n", |
81 | | - "\n", |
82 | | - "```{note}\n", |
83 | | - "In a normalized design, each row of a given table describes a distinct entity, and no two rows in that table represent different types of entities.\n", |
84 | | - "```\n", |
| 27 | + "## Key Requirements\n", |
85 | 28 | "\n", |
86 | | - "The table name (and its documentation) must clearly indicate what entity type is represented by the table's rows. We follow the convention whereby the table name must describe in singular form what each row represents. Thus a table describing database users might be named `User`.\n", |
87 | | - "Each table must have a primary key: the attributes that uniquely identify each entity in the table and in the real world. \n", |
88 | | - "Besides the primary key, each table may have secondary attributes. The secondary attributes must directly describe the entities of the table's class. In fully-normalized designs, the secondary attributes apply to each entity.\n", |
| 29 | + "1. **Clear Entity Representation**: The table name must clearly indicate what entity type is represented by the table's rows (using singular form)\n", |
| 30 | + "2. **Primary Key**: Each table must have a primary key that uniquely identifies each entity\n", |
| 31 | + "3. **Relevant Attributes**: Secondary attributes must directly describe the entities of the table's class\n", |
| 32 | + "4. **No Mixed Entities**: Avoid mixing different entity types in the same table\n", |
89 | 33 | "\n", |
90 | | - "## Example of unnormalized designs\n", |
91 | | - "SQL does not enforce normalization and most database tutorials are full of unnormalized designs. For example, SQL allows defining tables with no primary key, which allows storing duplicate entries. DataJoint table definition syntax presumes the existence of a primary key: one must only indicate the separation between the primary attributes comprising the primary key and the secondary attributes. \n", |
92 | | - "Leaving SQL behind, I will show a few unnormalized designs using DataJoint table definition notation and then normalize the design. \n", |
93 | | - "For example, consider a table for representing items in a shopping cart for an e-commerce site.\n", |
| 34 | + "## Example: E-commerce Shopping Cart\n", |
| 35 | + "Let's examine a common unnormalized design using DataJoint notation. Consider a table for representing items in a shopping cart for an e-commerce site:\n", |
94 | 36 | "\n", |
95 | 37 | "```\n", |
96 | 38 | ":: ShoppingCart\n", |
|
115 | 57 | "The typical novice mistake is to put too much information in the same table, mixing information about different entities in the same table. This table contains information describing multiple entities: orders, items, and buyers, all in one. \n", |
116 | 58 | "How would you fix this design?\n", |
117 | 59 | "\n", |
118 | | - "## Fixing it\n", |
119 | | - "Database normalization requires splitting unnormalized tables into multiple tables where each table describes its separate entity type. We separate the representations of the order, items, and items in the order. We will also establish dependencies between them.\n", |
120 | | - "Then we describe items that might be included in different orders. We will assume that the item price is specific for each order and will omit it from the item table. Then the only secondary field is `item_description`. \n", |
| 60 | + "## Normalized Solution\n", |
121 | 61 | "\n", |
| 62 | + "Database normalization requires splitting this into multiple tables, each representing a distinct entity type: \n", |
| 63 | + "\n", |
| 64 | + "### 1. Item Table\n", |
| 65 | + "(DataJoint)\n", |
| 66 | + "```python\n", |
| 67 | + "@schema\n", |
| 68 | + "class Item(dj.Manual):\n", |
| 69 | + " definition = \"\"\"\n", |
| 70 | + " item : int\n", |
| 71 | + " ---\n", |
| 72 | + " item_description : varchar(1000)\n", |
| 73 | + " \"\"\"\n", |
122 | 74 | "```\n", |
123 | | - "::Item \n", |
124 | | - "item : int\n", |
125 | | - "---\n", |
126 | | - "item_description : varchar(1000)\n", |
| 75 | + "(Equivalent SQL)\n", |
| 76 | + "```sql\n", |
| 77 | + "CREATE TABLE item (\n", |
| 78 | + " item INT,\n", |
| 79 | + " item_description VARCHAR(1000) NOT NULL\n", |
| 80 | + " PRIMARY KEY (item)\n", |
| 81 | + ");\n", |
127 | 82 | "```\n", |
128 | 83 | "\n", |
129 | | - "Then let's represent the general information about the order, not pertaining to each item:\n", |
130 | | - "```\n", |
131 | | - "::Order\n", |
132 | | - "order_number : int\n", |
133 | | - "---\n", |
134 | | - "purchase_date : date\n", |
135 | | - "buyer_full_name : varchar(16)\n", |
136 | | - "buyer_address : varchar(1000)\n", |
137 | | - "buyer_email : varchar(120)\n", |
138 | | - "total_amount : numeric(8, 2)\n", |
| 84 | + "### 2. Order Table\n", |
| 85 | + "\n", |
| 86 | + "(DataJoint)\n", |
| 87 | + "```python\n", |
| 88 | + "@schema\n", |
| 89 | + "class Order(dj.Manual):\n", |
| 90 | + " definition = \"\"\"\n", |
| 91 | + " order_number : int\n", |
| 92 | + " ---\n", |
| 93 | + " purchase_date : date\n", |
| 94 | + " buyer_full_name : varchar(16)\n", |
| 95 | + " buyer_address : varchar(1000)\n", |
| 96 | + " buyer_email : varchar(120)\n", |
| 97 | + " total_amount : numeric(8, 2)\n", |
| 98 | + " \"\"\"\n", |
139 | 99 | "```\n", |
140 | 100 | "\n", |
141 | | - "Finally, we specify the items in the order in a separate table, OrderItem . This table associates each item, its price and quantity, to the order.\n", |
| 101 | + "(Equivalent SQL)\n", |
| 102 | + "```sql\n", |
| 103 | + "CREATE TABLE order (\n", |
| 104 | + " order_number INT PRIMARY KEY,\n", |
| 105 | + " purchase_date DATE NOT NULL,\n", |
| 106 | + " buyer_full_name VARCHAR(16) NOT NULL,\n", |
| 107 | + "\n", |
| 108 | + "### 3. OrderItem Table (Junction Table), we specify the items in the order in a separate table, OrderItem . This table associates each item, its price and quantity, to the order.\n", |
142 | 109 | "\n", |
143 | 110 | "```\n", |
144 | 111 | "::OrderItem\n", |
|
162 | 129 | "item_quantity : int\n", |
163 | 130 | "```\n", |
164 | 131 | "\n", |
165 | | - "We can now plot the schema diagram:\n", |
| 132 | + "## Benefits of Normalized Design\n", |
166 | 133 | "\n", |
167 | | - "## Is the normalized design better?\n", |
| 134 | + "The normalized design provides several advantages:\n", |
168 | 135 | "\n", |
169 | | - "## Relationship to the classical normal forms " |
170 | | - ] |
171 | | - }, |
172 | | - { |
173 | | - "cell_type": "code", |
174 | | - "execution_count": null, |
175 | | - "metadata": {}, |
176 | | - "outputs": [], |
177 | | - "source": [ |
178 | | - "import datajoint as dj\n", |
179 | | - "schema = dj.schema('dimitri_university')" |
| 136 | + "1. **Eliminates Redundancy**: Item descriptions and buyer information stored once\n", |
| 137 | + "2. **Ensures Consistency**: Changes to item descriptions automatically apply everywhere\n", |
| 138 | + "3. **Prevents Anomalies**: No risk of inconsistent data across related records\n", |
| 139 | + "4. **Improves Performance**: Smaller tables with focused indexes\n", |
| 140 | + "5. **Enhances Maintainability**: Clear separation of concerns\n", |
| 141 | + "\n", |
| 142 | + "## Classical Normal Forms\n", |
| 143 | + "\n", |
| 144 | + "The normalization process follows specific rules called **normal forms**:\n", |
| 145 | + "\n", |
| 146 | + "- **First Normal Form (1NF)**: Eliminate repeating groups and ensure atomic values\n", |
| 147 | + "- **Second Normal Form (2NF)**: Remove partial dependencies (all non-key attributes depend on the entire primary key)\n", |
| 148 | + "- **Third Normal Form (3NF)**: Remove transitive dependencies (non-key attributes don't depend on other non-key attributes)\n", |
| 149 | + "\n", |
| 150 | + "The DataJoint approach enforces these principles by design, making it difficult to create unnormalized tables." |
180 | 151 | ] |
181 | 152 | }, |
182 | 153 | { |
183 | | - "cell_type": "code", |
184 | | - "execution_count": null, |
| 154 | + "cell_type": "markdown", |
185 | 155 | "metadata": {}, |
186 | | - "outputs": [], |
187 | 156 | "source": [] |
188 | 157 | } |
189 | 158 | ], |
|
0 commit comments