[{"data":1,"prerenderedAt":1234},["ShallowReactive",2],{"blog-\u002Fblog\u002Fsql-normalization-1nf-2nf-3nf":3},{"id":4,"title":5,"body":6,"description":1220,"difficulty":1221,"extension":1222,"framework":1223,"frameworkSlug":87,"meta":1224,"navigation":217,"order":151,"path":1225,"qaPath":1226,"seo":1227,"stem":1228,"subtopic":1229,"topic":1230,"topicSlug":1231,"updated":1232,"__hash__":1233},"blog\u002Fblog\u002Fsql-normalization-1nf-2nf-3nf.md","Database Normalization — 1NF, 2NF, 3NF, and When to Denormalize",{"type":7,"value":8,"toc":1210},"minimark",[9,14,27,31,34,45,56,60,67,83,329,339,343,354,376,391,542,546,557,580,585,774,781,785,1020,1024,1027,1192,1199,1203,1206],[10,11,13],"h2",{"id":12},"why-normalization-exists","Why normalization exists",[15,16,17,18,22,23,26],"p",{},"Normalization is a process of structuring a relational database to reduce data\nredundancy and prevent ",[19,20,21],"strong",{},"update anomalies"," — bugs where the same fact is stored\nin multiple places and they fall out of sync. The formal definition comes in\nnumbered ",[19,24,25],{},"normal forms"," (NF). In practice, reaching 3NF is the standard goal\nfor OLTP databases.",[10,28,30],{"id":29},"the-unnormalized-starting-point","The unnormalized starting point",[15,32,33],{},"Imagine you started with a single spreadsheet for orders:",[35,36,41],"pre",{"className":37,"code":39,"language":40},[38],"language-text","order_id | customer_name | customer_email       | product_ids   | product_names       | total\n---------|---------------|----------------------|---------------|---------------------|-------\n1001     | Alice Brown   | alice@example.com    | 42, 55        | Keyboard, Mouse     | 150.00\n1002     | Alice Brown   | alice@example.com    | 42            | Keyboard            | 75.00\n1003     | Bob Smith     | bob@example.com      | 55            | Mouse               | 75.00\n","text",[42,43,39],"code",{"__ignoreMap":44},"",[15,46,47,48,51,52,55],{},"Problems: Alice's email is duplicated. ",[42,49,50],{},"product_ids"," and ",[42,53,54],{},"product_names"," are\ncomma-lists in one cell. If Alice changes her email, we must update every row.\nIf a product is renamed, we must find every order containing it.",[10,57,59],{"id":58},"first-normal-form-1nf-no-repeating-groups","First Normal Form (1NF) — no repeating groups",[15,61,62,63,66],{},"1NF requires that each column holds a ",[19,64,65],{},"single atomic value"," — no comma-lists,\narrays, or JSON blobs where each element represents a separate fact.",[15,68,69,72,73,75,76,78,79,82],{},[19,70,71],{},"Fix:"," split ",[42,74,50],{}," \u002F ",[42,77,54],{}," into a separate ",[42,80,81],{},"order_items","\ntable with one row per product per order.",[35,84,88],{"className":85,"code":86,"language":87,"meta":44,"style":44},"language-sql shiki shiki-themes github-light github-dark","CREATE TABLE orders (\n    id           BIGINT       PRIMARY KEY,\n    customer_name  VARCHAR(100) NOT NULL,\n    customer_email VARCHAR(254) NOT NULL,\n    total_amount   NUMERIC(10,2) NOT NULL,\n    created_at     TIMESTAMPTZ  NOT NULL\n);\n\nCREATE TABLE order_items (\n    order_id    BIGINT NOT NULL REFERENCES orders(id),\n    product_id  INT    NOT NULL,\n    product_name VARCHAR(200) NOT NULL,\n    unit_price  NUMERIC(10,2) NOT NULL,\n    quantity    INT NOT NULL,\n    PRIMARY KEY (order_id, product_id)\n);\n","sql",[42,89,90,110,125,149,168,194,206,212,219,231,248,262,281,303,315,324],{"__ignoreMap":44},[91,92,95,99,102,106],"span",{"class":93,"line":94},"line",1,[91,96,98],{"class":97},"szBVR","CREATE",[91,100,101],{"class":97}," TABLE",[91,103,105],{"class":104},"sScJk"," orders",[91,107,109],{"class":108},"sVt8B"," (\n",[91,111,113,116,119,122],{"class":93,"line":112},2,[91,114,115],{"class":108},"    id           ",[91,117,118],{"class":97},"BIGINT",[91,120,121],{"class":97},"       PRIMARY KEY",[91,123,124],{"class":108},",\n",[91,126,128,131,134,137,141,144,147],{"class":93,"line":127},3,[91,129,130],{"class":108},"    customer_name  ",[91,132,133],{"class":97},"VARCHAR",[91,135,136],{"class":108},"(",[91,138,140],{"class":139},"sj4cs","100",[91,142,143],{"class":108},") ",[91,145,146],{"class":97},"NOT NULL",[91,148,124],{"class":108},[91,150,152,155,157,159,162,164,166],{"class":93,"line":151},4,[91,153,154],{"class":108},"    customer_email ",[91,156,133],{"class":97},[91,158,136],{"class":108},[91,160,161],{"class":139},"254",[91,163,143],{"class":108},[91,165,146],{"class":97},[91,167,124],{"class":108},[91,169,171,174,177,179,182,185,188,190,192],{"class":93,"line":170},5,[91,172,173],{"class":108},"    total_amount   ",[91,175,176],{"class":97},"NUMERIC",[91,178,136],{"class":108},[91,180,181],{"class":139},"10",[91,183,184],{"class":108},",",[91,186,187],{"class":139},"2",[91,189,143],{"class":108},[91,191,146],{"class":97},[91,193,124],{"class":108},[91,195,197,200,203],{"class":93,"line":196},6,[91,198,199],{"class":108},"    created_at     ",[91,201,202],{"class":97},"TIMESTAMPTZ",[91,204,205],{"class":97},"  NOT NULL\n",[91,207,209],{"class":93,"line":208},7,[91,210,211],{"class":108},");\n",[91,213,215],{"class":93,"line":214},8,[91,216,218],{"emptyLinePlaceholder":217},true,"\n",[91,220,222,224,226,229],{"class":93,"line":221},9,[91,223,98],{"class":97},[91,225,101],{"class":97},[91,227,228],{"class":104}," order_items",[91,230,109],{"class":108},[91,232,234,237,239,242,245],{"class":93,"line":233},10,[91,235,236],{"class":108},"    order_id    ",[91,238,118],{"class":97},[91,240,241],{"class":97}," NOT NULL",[91,243,244],{"class":97}," REFERENCES",[91,246,247],{"class":108}," orders(id),\n",[91,249,251,254,257,260],{"class":93,"line":250},11,[91,252,253],{"class":108},"    product_id  ",[91,255,256],{"class":97},"INT",[91,258,259],{"class":97},"    NOT NULL",[91,261,124],{"class":108},[91,263,265,268,270,272,275,277,279],{"class":93,"line":264},12,[91,266,267],{"class":108},"    product_name ",[91,269,133],{"class":97},[91,271,136],{"class":108},[91,273,274],{"class":139},"200",[91,276,143],{"class":108},[91,278,146],{"class":97},[91,280,124],{"class":108},[91,282,284,287,289,291,293,295,297,299,301],{"class":93,"line":283},13,[91,285,286],{"class":108},"    unit_price  ",[91,288,176],{"class":97},[91,290,136],{"class":108},[91,292,181],{"class":139},[91,294,184],{"class":108},[91,296,187],{"class":139},[91,298,143],{"class":108},[91,300,146],{"class":97},[91,302,124],{"class":108},[91,304,306,309,311,313],{"class":93,"line":305},14,[91,307,308],{"class":108},"    quantity    ",[91,310,256],{"class":97},[91,312,241],{"class":97},[91,314,124],{"class":108},[91,316,318,321],{"class":93,"line":317},15,[91,319,320],{"class":97},"    PRIMARY KEY",[91,322,323],{"class":108}," (order_id, product_id)\n",[91,325,327],{"class":93,"line":326},16,[91,328,211],{"class":108},[15,330,331,332,51,335,338],{},"Now each cell holds one value. But ",[42,333,334],{},"customer_name",[42,336,337],{},"customer_email"," are\nstill duplicated across every order for the same customer.",[10,340,342],{"id":341},"second-normal-form-2nf-no-partial-dependencies","Second Normal Form (2NF) — no partial dependencies",[15,344,345,346,349,350,353],{},"2NF applies to tables with ",[19,347,348],{},"composite primary keys",". It requires that every\nnon-key column depends on the ",[19,351,352],{},"whole"," key, not just part of it.",[15,355,356,357,359,360,363,364,367,368,371,372,375],{},"In ",[42,358,81],{},", ",[42,361,362],{},"product_name"," depends only on ",[42,365,366],{},"product_id"," (part of the\nkey), not on ",[42,369,370],{},"(order_id, product_id)",". That is a ",[19,373,374],{},"partial dependency"," — a\n2NF violation.",[15,377,378,380,381,383,384,387,388,390],{},[19,379,71],{}," move ",[42,382,362],{}," to a ",[42,385,386],{},"products"," table keyed on ",[42,389,366],{},".",[35,392,394],{"className":85,"code":393,"language":87,"meta":44,"style":44},"CREATE TABLE products (\n    id           INT          PRIMARY KEY,\n    product_name VARCHAR(200) NOT NULL,\n    unit_price   NUMERIC(10,2) NOT NULL\n);\n\nCREATE TABLE order_items (\n    order_id    BIGINT NOT NULL REFERENCES orders(id),\n    product_id  INT    NOT NULL REFERENCES products(id),\n    quantity    INT    NOT NULL,\n    unit_price  NUMERIC(10,2) NOT NULL,  -- price at time of order (snapshot)\n    PRIMARY KEY (order_id, product_id)\n);\n",[42,395,396,407,418,434,454,458,462,472,484,497,507,532,538],{"__ignoreMap":44},[91,397,398,400,402,405],{"class":93,"line":94},[91,399,98],{"class":97},[91,401,101],{"class":97},[91,403,404],{"class":104}," products",[91,406,109],{"class":108},[91,408,409,411,413,416],{"class":93,"line":112},[91,410,115],{"class":108},[91,412,256],{"class":97},[91,414,415],{"class":97},"          PRIMARY KEY",[91,417,124],{"class":108},[91,419,420,422,424,426,428,430,432],{"class":93,"line":127},[91,421,267],{"class":108},[91,423,133],{"class":97},[91,425,136],{"class":108},[91,427,274],{"class":139},[91,429,143],{"class":108},[91,431,146],{"class":97},[91,433,124],{"class":108},[91,435,436,439,441,443,445,447,449,451],{"class":93,"line":151},[91,437,438],{"class":108},"    unit_price   ",[91,440,176],{"class":97},[91,442,136],{"class":108},[91,444,181],{"class":139},[91,446,184],{"class":108},[91,448,187],{"class":139},[91,450,143],{"class":108},[91,452,453],{"class":97},"NOT NULL\n",[91,455,456],{"class":93,"line":170},[91,457,211],{"class":108},[91,459,460],{"class":93,"line":196},[91,461,218],{"emptyLinePlaceholder":217},[91,463,464,466,468,470],{"class":93,"line":208},[91,465,98],{"class":97},[91,467,101],{"class":97},[91,469,228],{"class":104},[91,471,109],{"class":108},[91,473,474,476,478,480,482],{"class":93,"line":214},[91,475,236],{"class":108},[91,477,118],{"class":97},[91,479,241],{"class":97},[91,481,244],{"class":97},[91,483,247],{"class":108},[91,485,486,488,490,492,494],{"class":93,"line":221},[91,487,253],{"class":108},[91,489,256],{"class":97},[91,491,259],{"class":97},[91,493,244],{"class":97},[91,495,496],{"class":108}," products(id),\n",[91,498,499,501,503,505],{"class":93,"line":233},[91,500,308],{"class":108},[91,502,256],{"class":97},[91,504,259],{"class":97},[91,506,124],{"class":108},[91,508,509,511,513,515,517,519,521,523,525,528],{"class":93,"line":250},[91,510,286],{"class":108},[91,512,176],{"class":97},[91,514,136],{"class":108},[91,516,181],{"class":139},[91,518,184],{"class":108},[91,520,187],{"class":139},[91,522,143],{"class":108},[91,524,146],{"class":97},[91,526,527],{"class":108},",  ",[91,529,531],{"class":530},"sJ8bj","-- price at time of order (snapshot)\n",[91,533,534,536],{"class":93,"line":264},[91,535,320],{"class":97},[91,537,323],{"class":108},[91,539,540],{"class":93,"line":283},[91,541,211],{"class":108},[10,543,545],{"id":544},"third-normal-form-3nf-no-transitive-dependencies","Third Normal Form (3NF) — no transitive dependencies",[15,547,548,549,552,553,556],{},"3NF requires that non-key columns depend ",[19,550,551],{},"only on the primary key",", not on\nother non-key columns (a ",[19,554,555],{},"transitive dependency",").",[15,558,559,560,563,564,566,567,569,570,573,574,576,577,579],{},"In the ",[42,561,562],{},"orders"," table, ",[42,565,337],{}," depends on ",[42,568,334],{},", not on\n",[42,571,572],{},"order_id",". If we store ",[42,575,334],{}," → ",[42,578,337],{}," in every order,\nupdating the email requires finding every order for that customer.",[15,581,582,584],{},[19,583,71],{}," extract customers into their own table.",[35,586,588],{"className":85,"code":587,"language":87,"meta":44,"style":44},"CREATE TABLE customers (\n    id         BIGINT       PRIMARY KEY GENERATED ALWAYS AS IDENTITY,\n    name       VARCHAR(100) NOT NULL,\n    email      VARCHAR(254) NOT NULL UNIQUE,\n    created_at TIMESTAMPTZ  NOT NULL DEFAULT NOW()\n);\n\nCREATE TABLE orders (\n    id           BIGINT        PRIMARY KEY GENERATED ALWAYS AS IDENTITY,\n    customer_id  BIGINT        NOT NULL REFERENCES customers(id),\n    total_amount NUMERIC(10,2) NOT NULL,\n    created_at   TIMESTAMPTZ   NOT NULL DEFAULT NOW()\n);\n",[42,589,590,601,624,642,662,681,685,689,699,718,733,754,770],{"__ignoreMap":44},[91,591,592,594,596,599],{"class":93,"line":94},[91,593,98],{"class":97},[91,595,101],{"class":97},[91,597,598],{"class":104}," customers",[91,600,109],{"class":108},[91,602,603,606,608,610,613,616,619,622],{"class":93,"line":112},[91,604,605],{"class":108},"    id         ",[91,607,118],{"class":97},[91,609,121],{"class":97},[91,611,612],{"class":97}," GENERATED",[91,614,615],{"class":97}," ALWAYS",[91,617,618],{"class":97}," AS",[91,620,621],{"class":97}," IDENTITY",[91,623,124],{"class":108},[91,625,626,629,632,634,636,638,640],{"class":93,"line":127},[91,627,628],{"class":97},"    name",[91,630,631],{"class":97},"       VARCHAR",[91,633,136],{"class":108},[91,635,140],{"class":139},[91,637,143],{"class":108},[91,639,146],{"class":97},[91,641,124],{"class":108},[91,643,644,647,649,651,653,655,657,660],{"class":93,"line":151},[91,645,646],{"class":108},"    email      ",[91,648,133],{"class":97},[91,650,136],{"class":108},[91,652,161],{"class":139},[91,654,143],{"class":108},[91,656,146],{"class":97},[91,658,659],{"class":97}," UNIQUE",[91,661,124],{"class":108},[91,663,664,667,669,672,675,678],{"class":93,"line":170},[91,665,666],{"class":108},"    created_at ",[91,668,202],{"class":97},[91,670,671],{"class":97},"  NOT NULL",[91,673,674],{"class":97}," DEFAULT",[91,676,677],{"class":97}," NOW",[91,679,680],{"class":108},"()\n",[91,682,683],{"class":93,"line":196},[91,684,211],{"class":108},[91,686,687],{"class":93,"line":208},[91,688,218],{"emptyLinePlaceholder":217},[91,690,691,693,695,697],{"class":93,"line":214},[91,692,98],{"class":97},[91,694,101],{"class":97},[91,696,105],{"class":104},[91,698,109],{"class":108},[91,700,701,703,705,708,710,712,714,716],{"class":93,"line":221},[91,702,115],{"class":108},[91,704,118],{"class":97},[91,706,707],{"class":97},"        PRIMARY KEY",[91,709,612],{"class":97},[91,711,615],{"class":97},[91,713,618],{"class":97},[91,715,621],{"class":97},[91,717,124],{"class":108},[91,719,720,723,725,728,730],{"class":93,"line":233},[91,721,722],{"class":108},"    customer_id  ",[91,724,118],{"class":97},[91,726,727],{"class":97},"        NOT NULL",[91,729,244],{"class":97},[91,731,732],{"class":108}," customers(id),\n",[91,734,735,738,740,742,744,746,748,750,752],{"class":93,"line":250},[91,736,737],{"class":108},"    total_amount ",[91,739,176],{"class":97},[91,741,136],{"class":108},[91,743,181],{"class":139},[91,745,184],{"class":108},[91,747,187],{"class":139},[91,749,143],{"class":108},[91,751,146],{"class":97},[91,753,124],{"class":108},[91,755,756,759,761,764,766,768],{"class":93,"line":264},[91,757,758],{"class":108},"    created_at   ",[91,760,202],{"class":97},[91,762,763],{"class":97},"   NOT NULL",[91,765,674],{"class":97},[91,767,677],{"class":97},[91,769,680],{"class":108},[91,771,772],{"class":93,"line":283},[91,773,211],{"class":108},[15,775,776,777,780],{},"Now updating a customer's email requires changing exactly one row in ",[42,778,779],{},"customers",".\nAll their orders automatically reflect the new email via the join.",[10,782,784],{"id":783},"the-final-3nf-schema","The final 3NF schema",[35,786,788],{"className":85,"code":787,"language":87,"meta":44,"style":44},"-- customers (one row per customer)\n-- products  (one row per product)\n-- orders    (one row per order, references customers)\n-- order_items (one row per product per order, references orders + products)\n\n-- Query: full order details\nSELECT\n    o.id             AS order_id,\n    c.name           AS customer_name,\n    p.product_name,\n    oi.quantity,\n    oi.unit_price,\n    oi.quantity * oi.unit_price AS line_total\nFROM orders      o\nJOIN customers   c  ON c.id = o.customer_id\nJOIN order_items oi ON oi.order_id = o.id\nJOIN products    p  ON p.id = oi.product_id\nWHERE o.id = 1001;\n",[42,789,790,795,800,805,810,814,819,824,840,856,867,879,890,913,921,950,974,1000],{"__ignoreMap":44},[91,791,792],{"class":93,"line":94},[91,793,794],{"class":530},"-- customers (one row per customer)\n",[91,796,797],{"class":93,"line":112},[91,798,799],{"class":530},"-- products  (one row per product)\n",[91,801,802],{"class":93,"line":127},[91,803,804],{"class":530},"-- orders    (one row per order, references customers)\n",[91,806,807],{"class":93,"line":151},[91,808,809],{"class":530},"-- order_items (one row per product per order, references orders + products)\n",[91,811,812],{"class":93,"line":170},[91,813,218],{"emptyLinePlaceholder":217},[91,815,816],{"class":93,"line":196},[91,817,818],{"class":530},"-- Query: full order details\n",[91,820,821],{"class":93,"line":208},[91,822,823],{"class":97},"SELECT\n",[91,825,826,829,831,834,837],{"class":93,"line":214},[91,827,828],{"class":139},"    o",[91,830,390],{"class":108},[91,832,833],{"class":139},"id",[91,835,836],{"class":97},"             AS",[91,838,839],{"class":108}," order_id,\n",[91,841,842,845,847,850,853],{"class":93,"line":221},[91,843,844],{"class":139},"    c",[91,846,390],{"class":108},[91,848,849],{"class":139},"name",[91,851,852],{"class":97},"           AS",[91,854,855],{"class":108}," customer_name,\n",[91,857,858,861,863,865],{"class":93,"line":233},[91,859,860],{"class":139},"    p",[91,862,390],{"class":108},[91,864,362],{"class":139},[91,866,124],{"class":108},[91,868,869,872,874,877],{"class":93,"line":250},[91,870,871],{"class":139},"    oi",[91,873,390],{"class":108},[91,875,876],{"class":139},"quantity",[91,878,124],{"class":108},[91,880,881,883,885,888],{"class":93,"line":264},[91,882,871],{"class":139},[91,884,390],{"class":108},[91,886,887],{"class":139},"unit_price",[91,889,124],{"class":108},[91,891,892,894,896,898,901,904,906,908,910],{"class":93,"line":283},[91,893,871],{"class":139},[91,895,390],{"class":108},[91,897,876],{"class":139},[91,899,900],{"class":97}," *",[91,902,903],{"class":139}," oi",[91,905,390],{"class":108},[91,907,887],{"class":139},[91,909,618],{"class":97},[91,911,912],{"class":108}," line_total\n",[91,914,915,918],{"class":93,"line":305},[91,916,917],{"class":97},"FROM",[91,919,920],{"class":108}," orders      o\n",[91,922,923,926,929,932,935,937,939,942,945,947],{"class":93,"line":317},[91,924,925],{"class":97},"JOIN",[91,927,928],{"class":108}," customers   c  ",[91,930,931],{"class":97},"ON",[91,933,934],{"class":139}," c",[91,936,390],{"class":108},[91,938,833],{"class":139},[91,940,941],{"class":97}," =",[91,943,944],{"class":139}," o",[91,946,390],{"class":108},[91,948,949],{"class":139},"customer_id\n",[91,951,952,954,957,959,961,963,965,967,969,971],{"class":93,"line":326},[91,953,925],{"class":97},[91,955,956],{"class":108}," order_items oi ",[91,958,931],{"class":97},[91,960,903],{"class":139},[91,962,390],{"class":108},[91,964,572],{"class":139},[91,966,941],{"class":97},[91,968,944],{"class":139},[91,970,390],{"class":108},[91,972,973],{"class":139},"id\n",[91,975,977,979,982,984,987,989,991,993,995,997],{"class":93,"line":976},17,[91,978,925],{"class":97},[91,980,981],{"class":108}," products    p  ",[91,983,931],{"class":97},[91,985,986],{"class":139}," p",[91,988,390],{"class":108},[91,990,833],{"class":139},[91,992,941],{"class":97},[91,994,903],{"class":139},[91,996,390],{"class":108},[91,998,999],{"class":139},"product_id\n",[91,1001,1003,1006,1008,1010,1012,1014,1017],{"class":93,"line":1002},18,[91,1004,1005],{"class":97},"WHERE",[91,1007,944],{"class":139},[91,1009,390],{"class":108},[91,1011,833],{"class":139},[91,1013,941],{"class":97},[91,1015,1016],{"class":139}," 1001",[91,1018,1019],{"class":108},";\n",[10,1021,1023],{"id":1022},"when-to-denormalize","When to denormalize",[15,1025,1026],{},"Normalization is right for OLTP (transactional writes). For analytics and\nreporting (OLAP), denormalization trades write anomaly risk for read performance.",[35,1028,1030],{"className":85,"code":1029,"language":87,"meta":44,"style":44},"-- Denormalized analytics table (pre-joined, no further joins needed)\nCREATE TABLE order_facts AS\nSELECT\n    o.id,\n    o.created_at,\n    c.country_code,\n    p.category,\n    oi.quantity * oi.unit_price AS line_revenue\nFROM orders o\nJOIN customers   c  ON c.id = o.customer_id\nJOIN order_items oi ON oi.order_id = o.id\nJOIN products    p  ON p.id = oi.product_id;\n",[42,1031,1032,1037,1049,1053,1063,1074,1085,1096,1117,1124,1146,1168],{"__ignoreMap":44},[91,1033,1034],{"class":93,"line":94},[91,1035,1036],{"class":530},"-- Denormalized analytics table (pre-joined, no further joins needed)\n",[91,1038,1039,1041,1043,1046],{"class":93,"line":112},[91,1040,98],{"class":97},[91,1042,101],{"class":97},[91,1044,1045],{"class":104}," order_facts",[91,1047,1048],{"class":97}," AS\n",[91,1050,1051],{"class":93,"line":127},[91,1052,823],{"class":97},[91,1054,1055,1057,1059,1061],{"class":93,"line":151},[91,1056,828],{"class":139},[91,1058,390],{"class":108},[91,1060,833],{"class":139},[91,1062,124],{"class":108},[91,1064,1065,1067,1069,1072],{"class":93,"line":170},[91,1066,828],{"class":139},[91,1068,390],{"class":108},[91,1070,1071],{"class":139},"created_at",[91,1073,124],{"class":108},[91,1075,1076,1078,1080,1083],{"class":93,"line":196},[91,1077,844],{"class":139},[91,1079,390],{"class":108},[91,1081,1082],{"class":139},"country_code",[91,1084,124],{"class":108},[91,1086,1087,1089,1091,1094],{"class":93,"line":208},[91,1088,860],{"class":139},[91,1090,390],{"class":108},[91,1092,1093],{"class":139},"category",[91,1095,124],{"class":108},[91,1097,1098,1100,1102,1104,1106,1108,1110,1112,1114],{"class":93,"line":214},[91,1099,871],{"class":139},[91,1101,390],{"class":108},[91,1103,876],{"class":139},[91,1105,900],{"class":97},[91,1107,903],{"class":139},[91,1109,390],{"class":108},[91,1111,887],{"class":139},[91,1113,618],{"class":97},[91,1115,1116],{"class":108}," line_revenue\n",[91,1118,1119,1121],{"class":93,"line":221},[91,1120,917],{"class":97},[91,1122,1123],{"class":108}," orders o\n",[91,1125,1126,1128,1130,1132,1134,1136,1138,1140,1142,1144],{"class":93,"line":233},[91,1127,925],{"class":97},[91,1129,928],{"class":108},[91,1131,931],{"class":97},[91,1133,934],{"class":139},[91,1135,390],{"class":108},[91,1137,833],{"class":139},[91,1139,941],{"class":97},[91,1141,944],{"class":139},[91,1143,390],{"class":108},[91,1145,949],{"class":139},[91,1147,1148,1150,1152,1154,1156,1158,1160,1162,1164,1166],{"class":93,"line":250},[91,1149,925],{"class":97},[91,1151,956],{"class":108},[91,1153,931],{"class":97},[91,1155,903],{"class":139},[91,1157,390],{"class":108},[91,1159,572],{"class":139},[91,1161,941],{"class":97},[91,1163,944],{"class":139},[91,1165,390],{"class":108},[91,1167,973],{"class":139},[91,1169,1170,1172,1174,1176,1178,1180,1182,1184,1186,1188,1190],{"class":93,"line":264},[91,1171,925],{"class":97},[91,1173,981],{"class":108},[91,1175,931],{"class":97},[91,1177,986],{"class":139},[91,1179,390],{"class":108},[91,1181,833],{"class":139},[91,1183,941],{"class":97},[91,1185,903],{"class":139},[91,1187,390],{"class":108},[91,1189,366],{"class":139},[91,1191,1019],{"class":108},[15,1193,1194,1195,1198],{},"This table is a ",[19,1196,1197],{},"snapshot"," — data warehouse tables, materialized views, and\nreporting replicas are acceptable places for denormalization. The normalized\nsource tables remain the system of record for writes.",[10,1200,1202],{"id":1201},"recap","Recap",[15,1204,1205],{},"Normalize your OLTP schema to at least 3NF: 1NF eliminates repeating groups\n(no arrays in cells); 2NF eliminates partial dependencies (non-key columns must\ndepend on the whole key); 3NF eliminates transitive dependencies (non-key\ncolumns must depend only on the primary key). Denormalization is a deliberate\noptimisation for read-heavy workloads — always denormalize a copy, not the\nsource of truth.",[1207,1208,1209],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":44,"searchDepth":112,"depth":112,"links":1211},[1212,1213,1214,1215,1216,1217,1218,1219],{"id":12,"depth":112,"text":13},{"id":29,"depth":112,"text":30},{"id":58,"depth":112,"text":59},{"id":341,"depth":112,"text":342},{"id":544,"depth":112,"text":545},{"id":783,"depth":112,"text":784},{"id":1022,"depth":112,"text":1023},{"id":1201,"depth":112,"text":1202},"Database normalization explained with a real e-commerce schema — 1NF, 2NF, 3NF normal forms, update anomalies, and when denormalization is the right call.","medium","md","SQL",{},"\u002Fblog\u002Fsql-normalization-1nf-2nf-3nf","\u002Fsql\u002Fschema\u002Fnormalization",{"title":5,"description":1220},"blog\u002Fsql-normalization-1nf-2nf-3nf","Normalization","Schema & Data Types","schema","2026-06-20","9RW6_ZXJMSSFoqlSfv2nbU9CCR0vGb-yHFDqHarAQQs",1782244089295]