[{"data":1,"prerenderedAt":2742},["ShallowReactive",2],{"post:\u002Fposts\u002Fmysql\u002Fmysql-learning-09":3,"posts:ordered":2462},{"id":4,"title":5,"body":6,"category":2446,"cover":2447,"date":2448,"description":2449,"extension":2450,"featured":2451,"meta":2452,"navigation":314,"path":2453,"seo":2454,"series":2455,"seriesOrder":349,"stem":2456,"tags":2457,"__hash__":2461},"posts\u002Fposts\u002Fmysql\u002Fmysql-learning-09.md","MySQL 学习笔记 09：约束与数据完整性",{"type":7,"value":8,"toc":2429},"minimark",[9,16,21,26,31,76,80,112,116,174,178,217,220,224,227,230,246,249,508,511,522,524,528,532,537,622,625,657,660,761,763,774,777,830,832,836,841,950,952,986,988,1078,1081,1247,1249,1260,1263,1279,1281,1285,1290,1372,1374,1399,1401,1535,1538,1597,1599,1610,1612,1616,1660,1662,1808,1810,1847,1849,1994,1997,2036,2039,2067,2125,2127,2138,2140,2144,2213,2216,2333,2336,2347,2349,2353,2414,2417,2425],[10,11,12],"blockquote",{},[13,14,15],"p",{},"本文是《SQL 学习笔记》系列的一篇：示例数据和 SQL 都是我整理并在 MySQL 8 上跑过的，讲解部分按自己的理解写的。",[17,18,20],"h1",{"id":19},"_12-sql-约束-constraints-与数据完整性","12 SQL 约束 (Constraints) 与数据完整性",[22,23,25],"h2",{"id":24},"_120-示例表结构","12.0 示例表结构",[27,28,30],"h3",{"id":29},"publishers-表父表","Publishers 表（父表）",[32,33,34,50],"table",{},[35,36,37],"thead",{},[38,39,40,44,47],"tr",{},[41,42,43],"th",{},"PublisherId",[41,45,46],{},"PublisherName",[41,48,49],{},"CountryCode",[51,52,53,65],"tbody",{},[38,54,55,59,62],{},[56,57,58],"td",{},"P001",[56,60,61],{},"Sunrise Press",[56,63,64],{},"SG",[38,66,67,70,73],{},[56,68,69],{},"P002",[56,71,72],{},"Northwind Books",[56,74,75],{},"US",[27,77,79],{"id":78},"publisherwebsite-表","PublisherWebsite 表",[32,81,82,95],{},[35,83,84],{},[38,85,86,89,92],{},[41,87,88],{},"Publisher",[41,90,91],{},"WebSite",[41,93,94],{},"Country",[51,96,97],{},[38,98,99,101,110],{},[56,100,61],{},[56,102,103],{},[104,105,109],"a",{"href":106,"rel":107},"http:\u002F\u002Fwww.timvideo.com",[108],"nofollow","www.timvideo.com",[56,111,64],{},[27,113,115],{"id":114},"stockadjustment-表","StockAdjustment 表",[32,117,118,138],{},[35,119,120],{},[38,121,122,125,129,132,135],{},[41,123,124],{},"BookCode",[41,126,128],{"align":127},"right","AdjustmentQty",[41,130,131],{},"DateAdjusted",[41,133,134],{},"WhoAdjust",[41,136,137],{},"AdjustReason",[51,139,140,157],{},[38,141,142,145,148,151,154],{},[56,143,144],{},"100",[56,146,147],{"align":127},"5",[56,149,150],{},"2026-10-01",[56,152,153],{},"Alice",[56,155,156],{},"Restock",[38,158,159,162,165,168,171],{},[56,160,161],{},"200",[56,163,164],{"align":127},"-3",[56,166,167],{},"2026-10-02",[56,169,170],{},"Bob",[56,172,173],{},"Damaged",[27,175,177],{"id":176},"orders-表依赖表","Orders 表（依赖表）",[32,179,180,193],{},[35,181,182],{},[38,183,184,187,190],{},[41,185,186],{"align":127},"OrderId",[41,188,189],{},"CustId",[41,191,192],{"align":127},"Amount",[51,194,195,206],{},[38,196,197,200,203],{},[56,198,199],{"align":127},"1001",[56,201,202],{},"C1",[56,204,205],{"align":127},"99",[38,207,208,211,214],{},[56,209,210],{"align":127},"1002",[56,212,213],{},"C2",[56,215,216],{"align":127},"150",[218,219],"hr",{},[22,221,223],{"id":222},"_121-数据完整性丢失的情况","12.1 数据完整性丢失的情况",[13,225,226],{},"数据完整性指的是数据在存储、修改、删除过程中始终保持正确、一致、有效。",[13,228,229],{},"原笔记列出：",[231,232,233,237,240,243],"ul",{},[234,235,236],"li",{},"无效数据添加到数据库。",[234,238,239],{},"现有数据被修改为不正确的值。",[234,241,242],{},"数据被错误删除。",[234,244,245],{},"在创建数据库对象时，SQL 可通过约束强制数据完整性。",[13,247,248],{},"注释与示例：",[250,251,256],"pre",{"className":252,"code":253,"language":254,"meta":255,"style":255},"language-sql shiki shiki-themes github-light github-dark","-- 情况 1：插入无效数据\nINSERT INTO StockAdjustment (BookCode, AdjustmentQty)\nVALUES (-1, 5);\n-- 如果 BookCode 表示商品编号，负数就是不合理的值\n\n-- 情况 2：把现有数据改成不正确值\nUPDATE StockAdjustment\nSET AdjustmentQty = 'abc'\nWHERE BookCode = 100;\n-- 类型不匹配，或数值超出业务允许范围\n\n-- 情况 3：错误删除数据\nDELETE FROM Customers\nWHERE CustomerId = 'C1';\n-- 如果 Orders 表中还有引用 C1 的订单，就会造成孤立数据\n\n-- 情况 4：插入重复主键\nINSERT INTO Publishers (PublisherId, PublisherName, CountryCode)\nVALUES ('P001', 'NewName', 'CN');\n-- P001 已存在，实体完整性被破坏\n\n-- 情况 5：插入不存在的引用\nINSERT INTO Orders (OrderId, CustId, Amount)\nVALUES (1003, 'C999', 50);\n-- C999 在 Customers 中不存在，引用完整性被破坏\n","sql","",[257,258,259,268,279,303,309,316,322,331,347,364,370,375,381,393,408,414,419,425,433,455,461,466,472,480,502],"code",{"__ignoreMap":255},[260,261,264],"span",{"class":262,"line":263},"line",1,[260,265,267],{"class":266},"sJ8bj","-- 情况 1：插入无效数据\n",[260,269,271,275],{"class":262,"line":270},2,[260,272,274],{"class":273},"szBVR","INSERT INTO",[260,276,278],{"class":277},"sVt8B"," StockAdjustment (BookCode, AdjustmentQty)\n",[260,280,282,285,288,291,295,298,300],{"class":262,"line":281},3,[260,283,284],{"class":273},"VALUES",[260,286,287],{"class":277}," (",[260,289,290],{"class":273},"-",[260,292,294],{"class":293},"sj4cs","1",[260,296,297],{"class":277},", ",[260,299,147],{"class":293},[260,301,302],{"class":277},");\n",[260,304,306],{"class":262,"line":305},4,[260,307,308],{"class":266},"-- 如果 BookCode 表示商品编号，负数就是不合理的值\n",[260,310,312],{"class":262,"line":311},5,[260,313,315],{"emptyLinePlaceholder":314},true,"\n",[260,317,319],{"class":262,"line":318},6,[260,320,321],{"class":266},"-- 情况 2：把现有数据改成不正确值\n",[260,323,325,328],{"class":262,"line":324},7,[260,326,327],{"class":273},"UPDATE",[260,329,330],{"class":277}," StockAdjustment\n",[260,332,334,337,340,343],{"class":262,"line":333},8,[260,335,336],{"class":273},"SET",[260,338,339],{"class":277}," AdjustmentQty ",[260,341,342],{"class":273},"=",[260,344,346],{"class":345},"sZZnC"," 'abc'\n",[260,348,350,353,356,358,361],{"class":262,"line":349},9,[260,351,352],{"class":273},"WHERE",[260,354,355],{"class":277}," BookCode ",[260,357,342],{"class":273},[260,359,360],{"class":293}," 100",[260,362,363],{"class":277},";\n",[260,365,367],{"class":262,"line":366},10,[260,368,369],{"class":266},"-- 类型不匹配，或数值超出业务允许范围\n",[260,371,373],{"class":262,"line":372},11,[260,374,315],{"emptyLinePlaceholder":314},[260,376,378],{"class":262,"line":377},12,[260,379,380],{"class":266},"-- 情况 3：错误删除数据\n",[260,382,384,387,390],{"class":262,"line":383},13,[260,385,386],{"class":273},"DELETE",[260,388,389],{"class":273}," FROM",[260,391,392],{"class":277}," Customers\n",[260,394,396,398,401,403,406],{"class":262,"line":395},14,[260,397,352],{"class":273},[260,399,400],{"class":277}," CustomerId ",[260,402,342],{"class":273},[260,404,405],{"class":345}," 'C1'",[260,407,363],{"class":277},[260,409,411],{"class":262,"line":410},15,[260,412,413],{"class":266},"-- 如果 Orders 表中还有引用 C1 的订单，就会造成孤立数据\n",[260,415,417],{"class":262,"line":416},16,[260,418,315],{"emptyLinePlaceholder":314},[260,420,422],{"class":262,"line":421},17,[260,423,424],{"class":266},"-- 情况 4：插入重复主键\n",[260,426,428,430],{"class":262,"line":427},18,[260,429,274],{"class":273},[260,431,432],{"class":277}," Publishers (PublisherId, PublisherName, CountryCode)\n",[260,434,436,438,440,443,445,448,450,453],{"class":262,"line":435},19,[260,437,284],{"class":273},[260,439,287],{"class":277},[260,441,442],{"class":345},"'P001'",[260,444,297],{"class":277},[260,446,447],{"class":345},"'NewName'",[260,449,297],{"class":277},[260,451,452],{"class":345},"'CN'",[260,454,302],{"class":277},[260,456,458],{"class":262,"line":457},20,[260,459,460],{"class":266},"-- P001 已存在，实体完整性被破坏\n",[260,462,464],{"class":262,"line":463},21,[260,465,315],{"emptyLinePlaceholder":314},[260,467,469],{"class":262,"line":468},22,[260,470,471],{"class":266},"-- 情况 5：插入不存在的引用\n",[260,473,475,477],{"class":262,"line":474},23,[260,476,274],{"class":273},[260,478,479],{"class":277}," Orders (OrderId, CustId, Amount)\n",[260,481,483,485,487,490,492,495,497,500],{"class":262,"line":482},24,[260,484,284],{"class":273},[260,486,287],{"class":277},[260,488,489],{"class":293},"1003",[260,491,297],{"class":277},[260,493,494],{"class":345},"'C999'",[260,496,297],{"class":277},[260,498,499],{"class":293},"50",[260,501,302],{"class":277},[260,503,505],{"class":262,"line":504},25,[260,506,507],{"class":266},"-- C999 在 Customers 中不存在，引用完整性被破坏\n",[13,509,510],{},"作用：",[231,512,513,516,519],{},[234,514,515],{},"约束在数据库层面拦截这些非法操作。",[234,517,518],{},"约束比应用层校验更可靠，因为所有程序都经过数据库。",[234,520,521],{},"约束减少数据修复成本。",[218,523],{},[22,525,527],{"id":526},"_122-约束类型","12.2 约束类型",[27,529,531],{"id":530},"_1221-required-data必需数据","12.2.1 Required Data（必需数据）",[231,533,534],{},[234,535,536],{},"通常转化为不可接受 NULL 值的字段。",[250,538,540],{"className":252,"code":539,"language":254,"meta":255,"style":255},"CREATE TABLE PublisherWebsite (\n  Publisher varchar(50) not null,\n  WebSite varchar(200) not null,\n  Country varchar(50),\n  PRIMARY KEY (Publisher)\n);\n",[257,541,542,557,579,596,610,618],{"__ignoreMap":255},[260,543,544,547,550,554],{"class":262,"line":263},[260,545,546],{"class":273},"CREATE",[260,548,549],{"class":273}," TABLE",[260,551,553],{"class":552},"sScJk"," PublisherWebsite",[260,555,556],{"class":277}," (\n",[260,558,559,562,565,568,570,573,576],{"class":262,"line":270},[260,560,561],{"class":277},"  Publisher ",[260,563,564],{"class":273},"varchar",[260,566,567],{"class":277},"(",[260,569,499],{"class":293},[260,571,572],{"class":277},") ",[260,574,575],{"class":273},"not null",[260,577,578],{"class":277},",\n",[260,580,581,584,586,588,590,592,594],{"class":262,"line":281},[260,582,583],{"class":277},"  WebSite ",[260,585,564],{"class":273},[260,587,567],{"class":277},[260,589,161],{"class":293},[260,591,572],{"class":277},[260,593,575],{"class":273},[260,595,578],{"class":277},[260,597,598,601,603,605,607],{"class":262,"line":305},[260,599,600],{"class":277},"  Country ",[260,602,564],{"class":273},[260,604,567],{"class":277},[260,606,499],{"class":293},[260,608,609],{"class":277},"),\n",[260,611,612,615],{"class":262,"line":311},[260,613,614],{"class":273},"  PRIMARY KEY",[260,616,617],{"class":277}," (Publisher)\n",[260,619,620],{"class":262,"line":318},[260,621,302],{"class":277},[13,623,624],{},"注释：",[231,626,627,632,640,648],{},[234,628,629,631],{},[257,630,575],{}," 表示该列必须有值。",[234,633,634,636,637,639],{},[257,635,88],{},"、",[257,638,91],{}," 不能为 NULL。",[234,641,642,644,645,647],{},[257,643,94],{}," 没有写 ",[257,646,575],{},"，所以允许为 NULL。",[234,649,650,653,654,656],{},[257,651,652],{},"PRIMARY KEY (Publisher)"," 同时要求 ",[257,655,88],{}," 唯一且非空。",[13,658,659],{},"执行效果：",[250,661,663],{"className":252,"code":662,"language":254,"meta":255,"style":255},"-- 成功\nINSERT INTO PublisherWebsite (Publisher, WebSite, Country)\nVALUES ('Sunrise Press', 'www.timvideo.com', 'SG');\n\n-- 失败：Publisher 为 NULL\nINSERT INTO PublisherWebsite (Publisher, WebSite)\nVALUES (NULL, 'www.example.com');\n\n-- 成功：Country 为 NULL\nINSERT INTO PublisherWebsite (Publisher, WebSite)\nVALUES ('Northwind Books', 'www.abc.com');\n",[257,664,665,670,677,698,702,707,714,730,734,739,745],{"__ignoreMap":255},[260,666,667],{"class":262,"line":263},[260,668,669],{"class":266},"-- 成功\n",[260,671,672,674],{"class":262,"line":270},[260,673,274],{"class":273},[260,675,676],{"class":277}," PublisherWebsite (Publisher, WebSite, Country)\n",[260,678,679,681,683,686,688,691,693,696],{"class":262,"line":281},[260,680,284],{"class":273},[260,682,287],{"class":277},[260,684,685],{"class":345},"'Sunrise Press'",[260,687,297],{"class":277},[260,689,690],{"class":345},"'www.timvideo.com'",[260,692,297],{"class":277},[260,694,695],{"class":345},"'SG'",[260,697,302],{"class":277},[260,699,700],{"class":262,"line":305},[260,701,315],{"emptyLinePlaceholder":314},[260,703,704],{"class":262,"line":311},[260,705,706],{"class":266},"-- 失败：Publisher 为 NULL\n",[260,708,709,711],{"class":262,"line":318},[260,710,274],{"class":273},[260,712,713],{"class":277}," PublisherWebsite (Publisher, WebSite)\n",[260,715,716,718,720,723,725,728],{"class":262,"line":324},[260,717,284],{"class":273},[260,719,287],{"class":277},[260,721,722],{"class":273},"NULL",[260,724,297],{"class":277},[260,726,727],{"class":345},"'www.example.com'",[260,729,302],{"class":277},[260,731,732],{"class":262,"line":333},[260,733,315],{"emptyLinePlaceholder":314},[260,735,736],{"class":262,"line":349},[260,737,738],{"class":266},"-- 成功：Country 为 NULL\n",[260,740,741,743],{"class":262,"line":366},[260,742,274],{"class":273},[260,744,713],{"class":277},[260,746,747,749,751,754,756,759],{"class":262,"line":372},[260,748,284],{"class":273},[260,750,287],{"class":277},[260,752,753],{"class":345},"'Northwind Books'",[260,755,297],{"class":277},[260,757,758],{"class":345},"'www.abc.com'",[260,760,302],{"class":277},[13,762,510],{},[231,764,765,768,771],{},[234,766,767],{},"保证关键字段必须有值。",[234,769,770],{},"防止业务数据缺失。",[234,772,773],{},"常用于主键、名称、状态、时间等字段。",[13,775,776],{},"常见可空性选择：",[32,778,779,789],{},[35,780,781],{},[38,782,783,786],{},[41,784,785],{},"场景",[41,787,788],{},"是否 NOT NULL",[51,790,791,799,807,814,822],{},[38,792,793,796],{},[56,794,795],{},"主键",[56,797,798],{},"是",[38,800,801,804],{},[56,802,803],{},"外键（可选关系）",[56,805,806],{},"否",[38,808,809,812],{},[56,810,811],{},"名称、标题",[56,813,798],{},[38,815,816,819],{},[56,817,818],{},"电话号码、备注",[56,820,821],{},"通常否",[38,823,824,827],{},[56,825,826],{},"创建时间",[56,828,829],{},"是（常用默认值）",[218,831],{},[27,833,835],{"id":834},"_1222-validity-checking有效性检查","12.2.2 Validity Checking（有效性检查）",[231,837,838],{},[234,839,840],{},"列具有特定范围或格式，通常使用 Check Constraint（检查约束）。",[250,842,844],{"className":252,"code":843,"language":254,"meta":255,"style":255},"CREATE TABLE StockAdjustment (\n  BookCode smallInt not null,\n  AdjustmentQty int,\n  DateAdjusted datetime,\n  WhoAdjust varchar(20),\n  AdjustReason varchar(50),\n  CONSTRAINT chk_book_code CHECK (BookCode BETWEEN 0 AND 99999)\n);\n",[257,845,846,857,870,880,890,904,917,946],{"__ignoreMap":255},[260,847,848,850,852,855],{"class":262,"line":263},[260,849,546],{"class":273},[260,851,549],{"class":273},[260,853,854],{"class":552}," StockAdjustment",[260,856,556],{"class":277},[260,858,859,862,865,868],{"class":262,"line":270},[260,860,861],{"class":277},"  BookCode ",[260,863,864],{"class":273},"smallInt",[260,866,867],{"class":273}," not null",[260,869,578],{"class":277},[260,871,872,875,878],{"class":262,"line":281},[260,873,874],{"class":277},"  AdjustmentQty ",[260,876,877],{"class":273},"int",[260,879,578],{"class":277},[260,881,882,885,888],{"class":262,"line":305},[260,883,884],{"class":277},"  DateAdjusted ",[260,886,887],{"class":273},"datetime",[260,889,578],{"class":277},[260,891,892,895,897,899,902],{"class":262,"line":311},[260,893,894],{"class":277},"  WhoAdjust ",[260,896,564],{"class":273},[260,898,567],{"class":277},[260,900,901],{"class":293},"20",[260,903,609],{"class":277},[260,905,906,909,911,913,915],{"class":262,"line":318},[260,907,908],{"class":277},"  AdjustReason ",[260,910,564],{"class":273},[260,912,567],{"class":277},[260,914,499],{"class":293},[260,916,609],{"class":277},[260,918,919,922,925,928,931,934,937,940,943],{"class":262,"line":324},[260,920,921],{"class":273},"  CONSTRAINT",[260,923,924],{"class":277}," chk_book_code ",[260,926,927],{"class":273},"CHECK",[260,929,930],{"class":277}," (BookCode ",[260,932,933],{"class":273},"BETWEEN",[260,935,936],{"class":293}," 0",[260,938,939],{"class":273}," AND",[260,941,942],{"class":293}," 99999",[260,944,945],{"class":277},")\n",[260,947,948],{"class":262,"line":333},[260,949,302],{"class":277},[13,951,624],{},[231,953,954,960,980],{},[234,955,956,959],{},[257,957,958],{},"BookCode smallInt not null","：图书编号，不能为空。",[234,961,962,965,966],{},[257,963,964],{},"chk_book_code CHECK (BookCode BETWEEN 0 AND 99999)","：\n",[231,967,968,975],{},[234,969,970,971,974],{},"约束名为 ",[257,972,973],{},"chk_book_code","。",[234,976,977,979],{},[257,978,124],{}," 必须在 0 到 99999 之间。",[234,981,982,985],{},[257,983,984],{},"BETWEEN 0 AND 99999"," 包含两端。",[13,987,659],{},[250,989,991],{"className":252,"code":990,"language":254,"meta":255,"style":255},"-- 成功\nINSERT INTO StockAdjustment (BookCode, AdjustmentQty)\nVALUES (100, 5);\n\n-- 失败：超出范围\nINSERT INTO StockAdjustment (BookCode, AdjustmentQty)\nVALUES (100000, 5);\n\n-- 失败：为负数\nINSERT INTO StockAdjustment (BookCode, AdjustmentQty)\nVALUES (-1, 5);\n",[257,992,993,997,1003,1017,1021,1026,1032,1047,1051,1056,1062],{"__ignoreMap":255},[260,994,995],{"class":262,"line":263},[260,996,669],{"class":266},[260,998,999,1001],{"class":262,"line":270},[260,1000,274],{"class":273},[260,1002,278],{"class":277},[260,1004,1005,1007,1009,1011,1013,1015],{"class":262,"line":281},[260,1006,284],{"class":273},[260,1008,287],{"class":277},[260,1010,144],{"class":293},[260,1012,297],{"class":277},[260,1014,147],{"class":293},[260,1016,302],{"class":277},[260,1018,1019],{"class":262,"line":305},[260,1020,315],{"emptyLinePlaceholder":314},[260,1022,1023],{"class":262,"line":311},[260,1024,1025],{"class":266},"-- 失败：超出范围\n",[260,1027,1028,1030],{"class":262,"line":318},[260,1029,274],{"class":273},[260,1031,278],{"class":277},[260,1033,1034,1036,1038,1041,1043,1045],{"class":262,"line":324},[260,1035,284],{"class":273},[260,1037,287],{"class":277},[260,1039,1040],{"class":293},"100000",[260,1042,297],{"class":277},[260,1044,147],{"class":293},[260,1046,302],{"class":277},[260,1048,1049],{"class":262,"line":333},[260,1050,315],{"emptyLinePlaceholder":314},[260,1052,1053],{"class":262,"line":349},[260,1054,1055],{"class":266},"-- 失败：为负数\n",[260,1057,1058,1060],{"class":262,"line":366},[260,1059,274],{"class":273},[260,1061,278],{"class":277},[260,1063,1064,1066,1068,1070,1072,1074,1076],{"class":262,"line":372},[260,1065,284],{"class":273},[260,1067,287],{"class":277},[260,1069,290],{"class":273},[260,1071,294],{"class":293},[260,1073,297],{"class":277},[260,1075,147],{"class":293},[260,1077,302],{"class":277},[13,1079,1080],{},"更多检查约束示例：",[250,1082,1084],{"className":252,"code":1083,"language":254,"meta":255,"style":255},"-- 数量必须大于 0\nCONSTRAINT chk_qty CHECK (AdjustmentQty > 0)\n\n-- 日期不能早于 2000-01-01\nCONSTRAINT chk_date CHECK (DateAdjusted >= '2000-01-01')\n\n-- 多列条件\nCONSTRAINT chk_code_range CHECK (BookCode >= 0 AND BookCode \u003C= 99999)\n\n-- 枚举式检查\nCONSTRAINT chk_status CHECK (Status IN ('Active', 'Inactive', 'Pending'))\n\n-- 字符串长度\nCONSTRAINT chk_name_len CHECK (CHAR_LENGTH(WhoAdjust) >= 2)\n",[257,1085,1086,1091,1111,1115,1120,1140,1144,1149,1175,1179,1184,1219,1223,1228],{"__ignoreMap":255},[260,1087,1088],{"class":262,"line":263},[260,1089,1090],{"class":266},"-- 数量必须大于 0\n",[260,1092,1093,1096,1099,1101,1104,1107,1109],{"class":262,"line":270},[260,1094,1095],{"class":273},"CONSTRAINT",[260,1097,1098],{"class":277}," chk_qty ",[260,1100,927],{"class":273},[260,1102,1103],{"class":277}," (AdjustmentQty ",[260,1105,1106],{"class":273},">",[260,1108,936],{"class":293},[260,1110,945],{"class":277},[260,1112,1113],{"class":262,"line":281},[260,1114,315],{"emptyLinePlaceholder":314},[260,1116,1117],{"class":262,"line":305},[260,1118,1119],{"class":266},"-- 日期不能早于 2000-01-01\n",[260,1121,1122,1124,1127,1129,1132,1135,1138],{"class":262,"line":311},[260,1123,1095],{"class":273},[260,1125,1126],{"class":277}," chk_date ",[260,1128,927],{"class":273},[260,1130,1131],{"class":277}," (DateAdjusted ",[260,1133,1134],{"class":273},">=",[260,1136,1137],{"class":345}," '2000-01-01'",[260,1139,945],{"class":277},[260,1141,1142],{"class":262,"line":318},[260,1143,315],{"emptyLinePlaceholder":314},[260,1145,1146],{"class":262,"line":324},[260,1147,1148],{"class":266},"-- 多列条件\n",[260,1150,1151,1153,1156,1158,1160,1162,1164,1166,1168,1171,1173],{"class":262,"line":333},[260,1152,1095],{"class":273},[260,1154,1155],{"class":277}," chk_code_range ",[260,1157,927],{"class":273},[260,1159,930],{"class":277},[260,1161,1134],{"class":273},[260,1163,936],{"class":293},[260,1165,939],{"class":273},[260,1167,355],{"class":277},[260,1169,1170],{"class":273},"\u003C=",[260,1172,942],{"class":293},[260,1174,945],{"class":277},[260,1176,1177],{"class":262,"line":349},[260,1178,315],{"emptyLinePlaceholder":314},[260,1180,1181],{"class":262,"line":366},[260,1182,1183],{"class":266},"-- 枚举式检查\n",[260,1185,1186,1188,1191,1193,1195,1198,1201,1203,1206,1208,1211,1213,1216],{"class":262,"line":372},[260,1187,1095],{"class":273},[260,1189,1190],{"class":277}," chk_status ",[260,1192,927],{"class":273},[260,1194,287],{"class":277},[260,1196,1197],{"class":273},"Status",[260,1199,1200],{"class":273}," IN",[260,1202,287],{"class":277},[260,1204,1205],{"class":345},"'Active'",[260,1207,297],{"class":277},[260,1209,1210],{"class":345},"'Inactive'",[260,1212,297],{"class":277},[260,1214,1215],{"class":345},"'Pending'",[260,1217,1218],{"class":277},"))\n",[260,1220,1221],{"class":262,"line":377},[260,1222,315],{"emptyLinePlaceholder":314},[260,1224,1225],{"class":262,"line":383},[260,1226,1227],{"class":266},"-- 字符串长度\n",[260,1229,1230,1232,1235,1237,1240,1242,1245],{"class":262,"line":395},[260,1231,1095],{"class":273},[260,1233,1234],{"class":277}," chk_name_len ",[260,1236,927],{"class":273},[260,1238,1239],{"class":277}," (CHAR_LENGTH(WhoAdjust) ",[260,1241,1134],{"class":273},[260,1243,1244],{"class":293}," 2",[260,1246,945],{"class":277},[13,1248,510],{},[231,1250,1251,1254,1257],{},[234,1252,1253],{},"限制列值在业务允许范围内。",[234,1255,1256],{},"防止明显错误的数据进入表。",[234,1258,1259],{},"减少应用层重复校验。",[13,1261,1262],{},"注意事项：",[231,1264,1265,1268,1274],{},[234,1266,1267],{},"检查约束在插入和更新时自动执行。",[234,1269,1270,1271,1273],{},"不同数据库对 ",[257,1272,927],{}," 支持程度不同，MySQL 8.0 以前支持较弱。",[234,1275,1276,1278],{},[257,1277,927],{}," 不应用于跨表校验，跨表校验通常用外键。",[218,1280],{},[27,1282,1284],{"id":1283},"_1223-entity-integrity-constraint实体完整性约束","12.2.3 Entity Integrity Constraint（实体完整性约束）",[231,1286,1287],{},[234,1288,1289],{},"表中每行在特定列上具有唯一值，通常使用 UNIQUE 约束或 PRIMARY KEY 约束。",[250,1291,1293],{"className":252,"code":1292,"language":254,"meta":255,"style":255},"CREATE TABLE Publishers (\n  PublisherId varchar(50) not null,\n  PublisherName varchar(50) not null UNIQUE,\n  CountryCode varchar(3) not null,\n  PRIMARY KEY (PublisherId, PublisherName)\n);\n",[257,1294,1295,1306,1323,1343,1361,1368],{"__ignoreMap":255},[260,1296,1297,1299,1301,1304],{"class":262,"line":263},[260,1298,546],{"class":273},[260,1300,549],{"class":273},[260,1302,1303],{"class":552}," Publishers",[260,1305,556],{"class":277},[260,1307,1308,1311,1313,1315,1317,1319,1321],{"class":262,"line":270},[260,1309,1310],{"class":277},"  PublisherId ",[260,1312,564],{"class":273},[260,1314,567],{"class":277},[260,1316,499],{"class":293},[260,1318,572],{"class":277},[260,1320,575],{"class":273},[260,1322,578],{"class":277},[260,1324,1325,1328,1330,1332,1334,1336,1338,1341],{"class":262,"line":281},[260,1326,1327],{"class":277},"  PublisherName ",[260,1329,564],{"class":273},[260,1331,567],{"class":277},[260,1333,499],{"class":293},[260,1335,572],{"class":277},[260,1337,575],{"class":273},[260,1339,1340],{"class":273}," UNIQUE",[260,1342,578],{"class":277},[260,1344,1345,1348,1350,1352,1355,1357,1359],{"class":262,"line":305},[260,1346,1347],{"class":277},"  CountryCode ",[260,1349,564],{"class":273},[260,1351,567],{"class":277},[260,1353,1354],{"class":293},"3",[260,1356,572],{"class":277},[260,1358,575],{"class":273},[260,1360,578],{"class":277},[260,1362,1363,1365],{"class":262,"line":311},[260,1364,614],{"class":273},[260,1366,1367],{"class":277}," (PublisherId, PublisherName)\n",[260,1369,1370],{"class":262,"line":318},[260,1371,302],{"class":277},[13,1373,624],{},[231,1375,1376,1382,1388,1393],{},[234,1377,1378,1381],{},[257,1379,1380],{},"PublisherId not null","：不能为空。",[234,1383,1384,1387],{},[257,1385,1386],{},"PublisherName not null UNIQUE","：不能为空，且值必须唯一。",[234,1389,1390,1381],{},[257,1391,1392],{},"CountryCode not null",[234,1394,1395,1398],{},[257,1396,1397],{},"PRIMARY KEY (PublisherId, PublisherName)","：复合主键，两列组合必须唯一且非空。",[13,1400,659],{},[250,1402,1404],{"className":252,"code":1403,"language":254,"meta":255,"style":255},"-- 成功\nINSERT INTO Publishers (PublisherId, PublisherName, CountryCode)\nVALUES ('P001', 'Sunrise Press', 'SG');\n\n-- 成功：PublisherId 重复但 PublisherName 不同，组合唯一\nINSERT INTO Publishers (PublisherId, PublisherName, CountryCode)\nVALUES ('P001', 'Northwind Books', 'US');\n\n-- 失败：PublisherName 重复，违反 UNIQUE\nINSERT INTO Publishers (PublisherId, PublisherName, CountryCode)\nVALUES ('P002', 'Sunrise Press', 'CN');\n\n-- 失败：PublisherId + PublisherName 组合重复，违反 PRIMARY KEY\nINSERT INTO Publishers (PublisherId, PublisherName, CountryCode)\nVALUES ('P001', 'Sunrise Press', 'US');\n",[257,1405,1406,1410,1416,1434,1438,1443,1449,1468,1472,1477,1483,1502,1506,1511,1517],{"__ignoreMap":255},[260,1407,1408],{"class":262,"line":263},[260,1409,669],{"class":266},[260,1411,1412,1414],{"class":262,"line":270},[260,1413,274],{"class":273},[260,1415,432],{"class":277},[260,1417,1418,1420,1422,1424,1426,1428,1430,1432],{"class":262,"line":281},[260,1419,284],{"class":273},[260,1421,287],{"class":277},[260,1423,442],{"class":345},[260,1425,297],{"class":277},[260,1427,685],{"class":345},[260,1429,297],{"class":277},[260,1431,695],{"class":345},[260,1433,302],{"class":277},[260,1435,1436],{"class":262,"line":305},[260,1437,315],{"emptyLinePlaceholder":314},[260,1439,1440],{"class":262,"line":311},[260,1441,1442],{"class":266},"-- 成功：PublisherId 重复但 PublisherName 不同，组合唯一\n",[260,1444,1445,1447],{"class":262,"line":318},[260,1446,274],{"class":273},[260,1448,432],{"class":277},[260,1450,1451,1453,1455,1457,1459,1461,1463,1466],{"class":262,"line":324},[260,1452,284],{"class":273},[260,1454,287],{"class":277},[260,1456,442],{"class":345},[260,1458,297],{"class":277},[260,1460,753],{"class":345},[260,1462,297],{"class":277},[260,1464,1465],{"class":345},"'US'",[260,1467,302],{"class":277},[260,1469,1470],{"class":262,"line":333},[260,1471,315],{"emptyLinePlaceholder":314},[260,1473,1474],{"class":262,"line":349},[260,1475,1476],{"class":266},"-- 失败：PublisherName 重复，违反 UNIQUE\n",[260,1478,1479,1481],{"class":262,"line":366},[260,1480,274],{"class":273},[260,1482,432],{"class":277},[260,1484,1485,1487,1489,1492,1494,1496,1498,1500],{"class":262,"line":372},[260,1486,284],{"class":273},[260,1488,287],{"class":277},[260,1490,1491],{"class":345},"'P002'",[260,1493,297],{"class":277},[260,1495,685],{"class":345},[260,1497,297],{"class":277},[260,1499,452],{"class":345},[260,1501,302],{"class":277},[260,1503,1504],{"class":262,"line":377},[260,1505,315],{"emptyLinePlaceholder":314},[260,1507,1508],{"class":262,"line":383},[260,1509,1510],{"class":266},"-- 失败：PublisherId + PublisherName 组合重复，违反 PRIMARY KEY\n",[260,1512,1513,1515],{"class":262,"line":395},[260,1514,274],{"class":273},[260,1516,432],{"class":277},[260,1518,1519,1521,1523,1525,1527,1529,1531,1533],{"class":262,"line":410},[260,1520,284],{"class":273},[260,1522,287],{"class":277},[260,1524,442],{"class":345},[260,1526,297],{"class":277},[260,1528,685],{"class":345},[260,1530,297],{"class":277},[260,1532,1465],{"class":345},[260,1534,302],{"class":277},[13,1536,1537],{},"主键与 UNIQUE 的区别：",[32,1539,1540,1553],{},[35,1541,1542],{},[38,1543,1544,1547,1550],{},[41,1545,1546],{},"特性",[41,1548,1549],{},"PRIMARY KEY",[41,1551,1552],{},"UNIQUE",[51,1554,1555,1566,1577,1586],{},[38,1556,1557,1560,1563],{},[56,1558,1559],{},"是否允许 NULL",[56,1561,1562],{},"不允许",[56,1564,1565],{},"通常允许一个或多个 NULL",[38,1567,1568,1571,1574],{},[56,1569,1570],{},"每表数量",[56,1572,1573],{},"只能一个",[56,1575,1576],{},"可以有多个",[38,1578,1579,1582,1584],{},[56,1580,1581],{},"是否自动建索引",[56,1583,798],{},[56,1585,798],{},[38,1587,1588,1591,1594],{},[56,1589,1590],{},"用途",[56,1592,1593],{},"唯一标识行",[56,1595,1596],{},"保证列值不重复",[13,1598,510],{},[231,1600,1601,1604,1607],{},[234,1602,1603],{},"保证每行可被唯一标识。",[234,1605,1606],{},"防止重复记录。",[234,1608,1609],{},"为外键提供引用目标。",[218,1611],{},[27,1613,1615],{"id":1614},"_1224-referential-integrity-constraint引用完整性约束","12.2.4 Referential Integrity Constraint（引用完整性约束）",[231,1617,1618,1621,1632],{},[234,1619,1620],{},"外键中的每个非空值必须在所引用的主键中有对应值。",[234,1622,1623,1624],{},"在依赖表中插入行或更新列时，必须满足：\n",[231,1625,1626,1629],{},[234,1627,1628],{},"父表中存在对应的主键值，或",[234,1630,1631],{},"外键值设置为 NULL。",[234,1633,1634,1635,1638,1639,1642,1643,1645,1646,1649,1650,1653,1654,1656,1657,1659],{},"示例：",[257,1636,1637],{},"CUSTOMER"," 为父表，",[257,1640,1641],{},"ORDER"," 为依赖表；插入 ",[257,1644,1641],{}," 的 ",[257,1647,1648],{},"customerID"," 为 ",[257,1651,1652],{},"C3"," 时，若 ",[257,1655,1637],{}," 中不存在 ",[257,1658,1652],{},"，则违反引用完整性。",[13,1661,1634],{},[250,1663,1665],{"className":252,"code":1664,"language":254,"meta":255,"style":255},"CREATE TABLE Customers (\n  CustomerId varchar(10) not null,\n  CustomerName varchar(50) not null,\n  PRIMARY KEY (CustomerId)\n);\n\nCREATE TABLE Orders (\n  OrderId int not null,\n  CustId varchar(10),\n  Amount decimal(10,2),\n  PRIMARY KEY (OrderId),\n  FOREIGN KEY (CustId) REFERENCES Customers(CustomerId)\n);\n",[257,1666,1667,1678,1696,1713,1720,1724,1728,1739,1750,1763,1783,1790,1804],{"__ignoreMap":255},[260,1668,1669,1671,1673,1676],{"class":262,"line":263},[260,1670,546],{"class":273},[260,1672,549],{"class":273},[260,1674,1675],{"class":552}," Customers",[260,1677,556],{"class":277},[260,1679,1680,1683,1685,1687,1690,1692,1694],{"class":262,"line":270},[260,1681,1682],{"class":277},"  CustomerId ",[260,1684,564],{"class":273},[260,1686,567],{"class":277},[260,1688,1689],{"class":293},"10",[260,1691,572],{"class":277},[260,1693,575],{"class":273},[260,1695,578],{"class":277},[260,1697,1698,1701,1703,1705,1707,1709,1711],{"class":262,"line":281},[260,1699,1700],{"class":277},"  CustomerName ",[260,1702,564],{"class":273},[260,1704,567],{"class":277},[260,1706,499],{"class":293},[260,1708,572],{"class":277},[260,1710,575],{"class":273},[260,1712,578],{"class":277},[260,1714,1715,1717],{"class":262,"line":305},[260,1716,614],{"class":273},[260,1718,1719],{"class":277}," (CustomerId)\n",[260,1721,1722],{"class":262,"line":311},[260,1723,302],{"class":277},[260,1725,1726],{"class":262,"line":318},[260,1727,315],{"emptyLinePlaceholder":314},[260,1729,1730,1732,1734,1737],{"class":262,"line":324},[260,1731,546],{"class":273},[260,1733,549],{"class":273},[260,1735,1736],{"class":552}," Orders",[260,1738,556],{"class":277},[260,1740,1741,1744,1746,1748],{"class":262,"line":333},[260,1742,1743],{"class":277},"  OrderId ",[260,1745,877],{"class":273},[260,1747,867],{"class":273},[260,1749,578],{"class":277},[260,1751,1752,1755,1757,1759,1761],{"class":262,"line":349},[260,1753,1754],{"class":277},"  CustId ",[260,1756,564],{"class":273},[260,1758,567],{"class":277},[260,1760,1689],{"class":293},[260,1762,609],{"class":277},[260,1764,1765,1768,1771,1773,1775,1778,1781],{"class":262,"line":366},[260,1766,1767],{"class":277},"  Amount ",[260,1769,1770],{"class":273},"decimal",[260,1772,567],{"class":277},[260,1774,1689],{"class":293},[260,1776,1777],{"class":277},",",[260,1779,1780],{"class":293},"2",[260,1782,609],{"class":277},[260,1784,1785,1787],{"class":262,"line":372},[260,1786,614],{"class":273},[260,1788,1789],{"class":277}," (OrderId),\n",[260,1791,1792,1795,1798,1801],{"class":262,"line":377},[260,1793,1794],{"class":273},"  FOREIGN KEY",[260,1796,1797],{"class":277}," (CustId) ",[260,1799,1800],{"class":273},"REFERENCES",[260,1802,1803],{"class":277}," Customers(CustomerId)\n",[260,1805,1806],{"class":262,"line":383},[260,1807,302],{"class":277},[13,1809,624],{},[231,1811,1812,1822,1831,1836],{},[234,1813,1814,1817,1818,1821],{},[257,1815,1816],{},"Customers"," 是父表，",[257,1819,1820],{},"CustomerId"," 是主键。",[234,1823,1824,1827,1828,1830],{},[257,1825,1826],{},"Orders"," 是依赖表，",[257,1829,189],{}," 是外键。",[234,1832,1833,1835],{},[257,1834,189],{}," 可以为 NULL，表示订单暂时没有关联客户。",[234,1837,1838,1839,1841,1842,1844,1845,974],{},"如果 ",[257,1840,189],{}," 非空，则必须在 ",[257,1843,1816],{}," 中存在对应 ",[257,1846,1820],{},[13,1848,659],{},[250,1850,1852],{"className":252,"code":1851,"language":254,"meta":255,"style":255},"-- 先插入父表数据\nINSERT INTO Customers (CustomerId, CustomerName)\nVALUES ('C1', 'Alice'), ('C2', 'Bob');\n\n-- 成功：C1 存在\nINSERT INTO Orders (OrderId, CustId, Amount)\nVALUES (1001, 'C1', 99);\n\n-- 成功：CustId 为 NULL\nINSERT INTO Orders (OrderId, CustId, Amount)\nVALUES (1002, NULL, 50);\n\n-- 失败：C3 在 Customers 中不存在\nINSERT INTO Orders (OrderId, CustId, Amount)\nVALUES (1003, 'C3', 80);\n",[257,1853,1854,1859,1866,1893,1897,1902,1908,1926,1930,1935,1941,1959,1963,1968,1974],{"__ignoreMap":255},[260,1855,1856],{"class":262,"line":263},[260,1857,1858],{"class":266},"-- 先插入父表数据\n",[260,1860,1861,1863],{"class":262,"line":270},[260,1862,274],{"class":273},[260,1864,1865],{"class":277}," Customers (CustomerId, CustomerName)\n",[260,1867,1868,1870,1872,1875,1877,1880,1883,1886,1888,1891],{"class":262,"line":281},[260,1869,284],{"class":273},[260,1871,287],{"class":277},[260,1873,1874],{"class":345},"'C1'",[260,1876,297],{"class":277},[260,1878,1879],{"class":345},"'Alice'",[260,1881,1882],{"class":277},"), (",[260,1884,1885],{"class":345},"'C2'",[260,1887,297],{"class":277},[260,1889,1890],{"class":345},"'Bob'",[260,1892,302],{"class":277},[260,1894,1895],{"class":262,"line":305},[260,1896,315],{"emptyLinePlaceholder":314},[260,1898,1899],{"class":262,"line":311},[260,1900,1901],{"class":266},"-- 成功：C1 存在\n",[260,1903,1904,1906],{"class":262,"line":318},[260,1905,274],{"class":273},[260,1907,479],{"class":277},[260,1909,1910,1912,1914,1916,1918,1920,1922,1924],{"class":262,"line":324},[260,1911,284],{"class":273},[260,1913,287],{"class":277},[260,1915,199],{"class":293},[260,1917,297],{"class":277},[260,1919,1874],{"class":345},[260,1921,297],{"class":277},[260,1923,205],{"class":293},[260,1925,302],{"class":277},[260,1927,1928],{"class":262,"line":333},[260,1929,315],{"emptyLinePlaceholder":314},[260,1931,1932],{"class":262,"line":349},[260,1933,1934],{"class":266},"-- 成功：CustId 为 NULL\n",[260,1936,1937,1939],{"class":262,"line":366},[260,1938,274],{"class":273},[260,1940,479],{"class":277},[260,1942,1943,1945,1947,1949,1951,1953,1955,1957],{"class":262,"line":372},[260,1944,284],{"class":273},[260,1946,287],{"class":277},[260,1948,210],{"class":293},[260,1950,297],{"class":277},[260,1952,722],{"class":273},[260,1954,297],{"class":277},[260,1956,499],{"class":293},[260,1958,302],{"class":277},[260,1960,1961],{"class":262,"line":377},[260,1962,315],{"emptyLinePlaceholder":314},[260,1964,1965],{"class":262,"line":383},[260,1966,1967],{"class":266},"-- 失败：C3 在 Customers 中不存在\n",[260,1969,1970,1972],{"class":262,"line":395},[260,1971,274],{"class":273},[260,1973,479],{"class":277},[260,1975,1976,1978,1980,1982,1984,1987,1989,1992],{"class":262,"line":410},[260,1977,284],{"class":273},[260,1979,287],{"class":277},[260,1981,489],{"class":293},[260,1983,297],{"class":277},[260,1985,1986],{"class":345},"'C3'",[260,1988,297],{"class":277},[260,1990,1991],{"class":293},"80",[260,1993,302],{"class":277},[13,1995,1996],{},"删除和更新行为：",[250,1998,2000],{"className":252,"code":1999,"language":254,"meta":255,"style":255},"-- 删除被引用的父表行\nDELETE FROM Customers WHERE CustomerId = 'C1';\n-- 如果 Orders 中有 CustId = 'C1' 的行，可能失败\n-- 错误：违反引用完整性\n",[257,2001,2002,2007,2026,2031],{"__ignoreMap":255},[260,2003,2004],{"class":262,"line":263},[260,2005,2006],{"class":266},"-- 删除被引用的父表行\n",[260,2008,2009,2011,2013,2016,2018,2020,2022,2024],{"class":262,"line":270},[260,2010,386],{"class":273},[260,2012,389],{"class":273},[260,2014,2015],{"class":277}," Customers ",[260,2017,352],{"class":273},[260,2019,400],{"class":277},[260,2021,342],{"class":273},[260,2023,405],{"class":345},[260,2025,363],{"class":277},[260,2027,2028],{"class":262,"line":281},[260,2029,2030],{"class":266},"-- 如果 Orders 中有 CustId = 'C1' 的行，可能失败\n",[260,2032,2033],{"class":262,"line":305},[260,2034,2035],{"class":266},"-- 错误：违反引用完整性\n",[13,2037,2038],{},"外键的引用动作：",[250,2040,2042],{"className":252,"code":2041,"language":254,"meta":255,"style":255},"FOREIGN KEY (CustId) REFERENCES Customers(CustomerId)\nON DELETE CASCADE\nON UPDATE CASCADE;\n",[257,2043,2044,2055,2060],{"__ignoreMap":255},[260,2045,2046,2049,2051,2053],{"class":262,"line":263},[260,2047,2048],{"class":273},"FOREIGN KEY",[260,2050,1797],{"class":277},[260,2052,1800],{"class":273},[260,2054,1803],{"class":277},[260,2056,2057],{"class":262,"line":270},[260,2058,2059],{"class":273},"ON DELETE CASCADE\n",[260,2061,2062,2065],{"class":262,"line":281},[260,2063,2064],{"class":273},"ON UPDATE CASCADE",[260,2066,363],{"class":277},[32,2068,2069,2079],{},[35,2070,2071],{},[38,2072,2073,2076],{},[41,2074,2075],{},"动作",[41,2077,2078],{},"含义",[51,2080,2081,2095,2105,2115],{},[38,2082,2083,2092],{},[56,2084,2085,2088,2089],{},[257,2086,2087],{},"RESTRICT"," \u002F ",[257,2090,2091],{},"NO ACTION",[56,2093,2094],{},"有引用时禁止删除或更新",[38,2096,2097,2102],{},[56,2098,2099],{},[257,2100,2101],{},"CASCADE",[56,2103,2104],{},"父表删除或更新时，子表自动删除或更新",[38,2106,2107,2112],{},[56,2108,2109],{},[257,2110,2111],{},"SET NULL",[56,2113,2114],{},"父表删除或更新时，子表外键设为 NULL",[38,2116,2117,2122],{},[56,2118,2119],{},[257,2120,2121],{},"SET DEFAULT",[56,2123,2124],{},"父表删除或更新时，子表外键设为默认值",[13,2126,510],{},[231,2128,2129,2132,2135],{},[234,2130,2131],{},"保证子表中的引用在父表中一定存在。",[234,2133,2134],{},"防止孤立数据。",[234,2136,2137],{},"维护表与表之间的一致性。",[218,2139],{},[22,2141,2143],{"id":2142},"_123-约束对比总结","12.3 约束对比总结",[32,2145,2146,2159],{},[35,2147,2148],{},[38,2149,2150,2153,2156],{},[41,2151,2152],{},"约束",[41,2154,2155],{},"作用",[41,2157,2158],{},"关键字",[51,2160,2161,2174,2186,2200],{},[38,2162,2163,2166,2169],{},[56,2164,2165],{},"必需数据",[56,2167,2168],{},"不允许 NULL",[56,2170,2171],{},[257,2172,2173],{},"NOT NULL",[38,2175,2176,2179,2182],{},[56,2177,2178],{},"有效性检查",[56,2180,2181],{},"限制取值范围或格式",[56,2183,2184],{},[257,2185,927],{},[38,2187,2188,2191,2194],{},[56,2189,2190],{},"实体完整性",[56,2192,2193],{},"保证行唯一标识",[56,2195,2196,636,2198],{},[257,2197,1549],{},[257,2199,1552],{},[38,2201,2202,2205,2208],{},[56,2203,2204],{},"引用完整性",[56,2206,2207],{},"保证外键引用有效",[56,2209,2210],{},[257,2211,2212],{},"FOREIGN KEY ... REFERENCES",[13,2214,2215],{},"约束命名示例：",[250,2217,2219],{"className":252,"code":2218,"language":254,"meta":255,"style":255},"CREATE TABLE StockAdjustment (\n  BookCode smallInt not null,\n  AdjustmentQty int,\n  DateAdjusted datetime,\n  WhoAdjust varchar(20),\n  AdjustReason varchar(50),\n  CONSTRAINT PK_StockAdjustment PRIMARY KEY (BookCode),\n  CONSTRAINT chk_book_code CHECK (BookCode BETWEEN 0 AND 99999),\n  CONSTRAINT chk_qty CHECK (AdjustmentQty > 0)\n);\n",[257,2220,2221,2231,2241,2249,2257,2269,2281,2293,2313,2329],{"__ignoreMap":255},[260,2222,2223,2225,2227,2229],{"class":262,"line":263},[260,2224,546],{"class":273},[260,2226,549],{"class":273},[260,2228,854],{"class":552},[260,2230,556],{"class":277},[260,2232,2233,2235,2237,2239],{"class":262,"line":270},[260,2234,861],{"class":277},[260,2236,864],{"class":273},[260,2238,867],{"class":273},[260,2240,578],{"class":277},[260,2242,2243,2245,2247],{"class":262,"line":281},[260,2244,874],{"class":277},[260,2246,877],{"class":273},[260,2248,578],{"class":277},[260,2250,2251,2253,2255],{"class":262,"line":305},[260,2252,884],{"class":277},[260,2254,887],{"class":273},[260,2256,578],{"class":277},[260,2258,2259,2261,2263,2265,2267],{"class":262,"line":311},[260,2260,894],{"class":277},[260,2262,564],{"class":273},[260,2264,567],{"class":277},[260,2266,901],{"class":293},[260,2268,609],{"class":277},[260,2270,2271,2273,2275,2277,2279],{"class":262,"line":318},[260,2272,908],{"class":277},[260,2274,564],{"class":273},[260,2276,567],{"class":277},[260,2278,499],{"class":293},[260,2280,609],{"class":277},[260,2282,2283,2285,2288,2290],{"class":262,"line":324},[260,2284,921],{"class":273},[260,2286,2287],{"class":277}," PK_StockAdjustment ",[260,2289,1549],{"class":273},[260,2291,2292],{"class":277}," (BookCode),\n",[260,2294,2295,2297,2299,2301,2303,2305,2307,2309,2311],{"class":262,"line":333},[260,2296,921],{"class":273},[260,2298,924],{"class":277},[260,2300,927],{"class":273},[260,2302,930],{"class":277},[260,2304,933],{"class":273},[260,2306,936],{"class":293},[260,2308,939],{"class":273},[260,2310,942],{"class":293},[260,2312,609],{"class":277},[260,2314,2315,2317,2319,2321,2323,2325,2327],{"class":262,"line":349},[260,2316,921],{"class":273},[260,2318,1098],{"class":277},[260,2320,927],{"class":273},[260,2322,1103],{"class":277},[260,2324,1106],{"class":273},[260,2326,936],{"class":293},[260,2328,945],{"class":277},[260,2330,2331],{"class":262,"line":366},[260,2332,302],{"class":277},[13,2334,2335],{},"命名好处：",[231,2337,2338,2341,2344],{},[234,2339,2340],{},"便于后续删除或修改约束。",[234,2342,2343],{},"报错信息更清晰。",[234,2345,2346],{},"数据库结构更易维护。",[218,2348],{},[22,2350,2352],{"id":2351},"_124-常见坑总结","12.4 常见坑总结",[2354,2355,2356,2361,2369,2374,2379,2382,2385,2388,2394,2397,2404,2411],"ol",{},[234,2357,2358,2360],{},[257,2359,2173],{}," 表示必须有值，不写则允许 NULL。",[234,2362,2363,2365,2366,2368],{},[257,2364,1549],{}," 不允许 NULL，",[257,2367,1552],{}," 通常允许 NULL。",[234,2370,2371,2372,974],{},"一个表只能有一个主键，但可以有多个 ",[257,2373,1552],{},[234,2375,2376,2378],{},[257,2377,927],{}," 用于单表范围校验，不用于跨表校验。",[234,2380,2381],{},"外键列可以为 NULL，表示暂时没有引用。",[234,2383,2384],{},"插入外键值前，父表中必须存在对应主键值。",[234,2386,2387],{},"删除父表行时，受外键引用动作影响。",[234,2389,2390,2393],{},[257,2391,2392],{},"ON DELETE CASCADE"," 可能造成大量数据被自动删除，要谨慎使用。",[234,2395,2396],{},"添加约束前，现有数据必须满足约束，否则添加失败。",[234,2398,2399,2400,2403],{},"约束应在建表时定义，也可通过 ",[257,2401,2402],{},"ALTER TABLE"," 添加。",[234,2405,1270,2406,636,2408,2410],{},[257,2407,927],{},[257,2409,1552],{},"、外键行为的支持有差异。",[234,2412,2413],{},"约束是数据库层面的最后防线，应用层校验不能替代约束。",[13,2415,2416],{},"核心记忆点：",[250,2418,2423],{"className":2419,"code":2421,"language":2422,"meta":255},[2420],"language-text","NOT NULL    → 必须有值\nCHECK       → 值必须在允许范围内\nPRIMARY KEY → 唯一且非空，标识一行\nUNIQUE      → 值不能重复\nFOREIGN KEY → 引用必须存在\n约束保证数据完整性，防止无效、重复、孤立数据。\n","text",[257,2424,2421],{"__ignoreMap":255},[2426,2427,2428],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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 pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":255,"searchDepth":281,"depth":281,"links":2430},[2431,2437,2438,2444,2445],{"id":24,"depth":270,"text":25,"children":2432},[2433,2434,2435,2436],{"id":29,"depth":281,"text":30},{"id":78,"depth":281,"text":79},{"id":114,"depth":281,"text":115},{"id":176,"depth":281,"text":177},{"id":222,"depth":270,"text":223},{"id":526,"depth":270,"text":527,"children":2439},[2440,2441,2442,2443],{"id":530,"depth":281,"text":531},{"id":834,"depth":281,"text":835},{"id":1283,"depth":281,"text":1284},{"id":1614,"depth":281,"text":1615},{"id":2142,"depth":270,"text":2143},{"id":2351,"depth":270,"text":2352},"学习",null,"2026-10-10","MySQL 学习笔记第九篇：SQL 约束——NOT NULL、CHECK、PRIMARY KEY、FOREIGN KEY 等如何保证数据完整性，以及它们的对比。","md",false,{},"\u002Fposts\u002Fmysql\u002Fmysql-learning-09",{"title":5,"description":2449},"SQL 学习笔记","posts\u002Fmysql\u002Fmysql-learning-09",[2458,2459,2460],"SQL","MySQL","学习笔记","3yKSQCCr2xDQZae5JG7LoBFQz16ZX3gUtf5_GYdexjA",[2463,2470,2476,2482,2487,2492,2497,2502,2507,2512,2517,2522,2527,2532,2537,2542,2547,2552,2557,2562,2567,2572,2577,2582,2587,2592,2597,2602,2607,2613,2617,2621,2625,2629,2633,2637,2641,2645,2649,2653,2657,2663,2667,2671,2675,2679,2683,2687,2688,2692,2696,2701,2705,2709,2714,2719,2725,2729,2734,2738],{"path":2464,"title":2465,"description":2466,"date":2467,"series":2468,"seriesOrder":2469},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-28","软件分析与设计笔记 28：draw.io 作图手册：五张图的符号位置、落笔顺序与自检","不模拟软件界面，只讲清三件事：每个 UML 符号在 draw.io 里搜什么词、一张图该按什么顺序落笔、画完拿什么标准自查。覆盖用例图、活动图、顺序图、类图、界面草图五种图纸，末尾附「最容易画反的十二笔」总检表。","2026-11-19","软件分析与设计笔记",28,{"path":2471,"title":2472,"description":2473,"date":2474,"series":2468,"seriesOrder":2475},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-27","软件分析与设计笔记 27：Paper 1 应试：从读案例到交卷的一次完整演算","拿一份完整的案例，从头到尾走一遍：怎么读案例列角色、怎么在四个小问之间分配时间、每一张图从哪一句话推出来、两张表怎么填、交卷前拿什么清单自查。附 C25 真题与样卷的题型分值对照。","2026-11-18",27,{"path":2477,"title":2478,"description":2479,"date":2480,"series":2468,"seriesOrder":2481},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-26","软件分析与设计笔记 26：总结：把整条链路串起来","系列收尾：从需求到代码的完整链路回顾、每一环的产出与验收方式、六条贯穿全系列的原则，以及一份可以直接拿去做项目自查的清单。","2026-11-17",26,{"path":2483,"title":2484,"description":2485,"date":2486,"series":2468,"seriesOrder":504},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-25","软件分析与设计笔记 25：代码重构：坏味道与手法","重构不改变外部行为、只改善内部结构：四种常见坏味道与对应手法、为什么动手之前必须先有测试、什么时候该重构，以及重构和前面那些设计模型是什么关系。","2026-11-16",{"path":2488,"title":2489,"description":2490,"date":2491,"series":2468,"seriesOrder":482},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-24","软件分析与设计笔记 24：设计原则：内聚、耦合与多态","好设计怎么衡量：内聚与耦合分别在说什么、一次从「差设计」到「多态重设计」的完整对照、抽象类与接口的选择依据，以及复用为什么会从优势变成风险。","2026-11-15",{"path":2493,"title":2494,"description":2495,"date":2496,"series":2468,"seriesOrder":474},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-23","软件分析与设计笔记 23：设计建模：从对象模型到能实现的方案","分析模型能用但还不能直接开工：对象模型怎么映射到关系模型、继承映射的三种方案怎么选、数据访问层与 Data Broker 解决什么问题，以及三层架构与 MVC 的分工。","2026-11-14",{"path":2498,"title":2499,"description":2500,"date":2501,"series":2468,"seriesOrder":468},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-22","软件分析与设计笔记 22：分析建模：边界、实体与控制对象","不经过分析直接写代码会踩什么坑、三种分析对象各管一段、怎么用顺序图完成一次用例实现，以及从顺序图导出类的属性、操作与关系的方法。","2026-11-13",{"path":2503,"title":2504,"description":2505,"date":2506,"series":2468,"seriesOrder":463},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-21","软件分析与设计笔记 21：UML：类图与顺序图","为什么需要一种共同的画法：类符号三格怎么画、五种关系的符号与设计含义、每种关系落到 Java 是什么写法，顺序图的三要素怎么摆；以及这三种最容易找不到的符号在 draw.io 里各搜什么词。","2026-11-12",{"path":2508,"title":2509,"description":2510,"date":2511,"series":2468,"seriesOrder":457},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-20","软件分析与设计笔记 20：面向对象概念：对象、关系与多态","面向对象不是「用某个语言写代码」：对象与类的区别、封装真正在保护什么、五种关系（依赖、关联、聚合、组合、继承）各自何时用，以及多态为什么能减少条件判断。","2026-11-11",{"path":2513,"title":2514,"description":2515,"date":2516,"series":2468,"seriesOrder":435},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-19","软件分析与设计笔记 19：规范化与逻辑数据模型","一次改地址引发的数据不一致，讲清规范化的真正动机；UNF 到 3NF 每一步在解决什么问题，派生字段为什么不能存，优化为什么和规范化方向相反，以及逻辑数据模型在 3NF 之上补了什么。","2026-11-10",{"path":2518,"title":2519,"description":2520,"date":2521,"series":2468,"seriesOrder":427},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-18","软件分析与设计笔记 18：数据库分析：实体、关系与数据字典","为什么不能拿到需求就动手建表：实体与关系怎么判定、基数为什么必须由业务方确认、数据命名要遵守什么规则，以及数据字典四列里最容易被空着的那一列。","2026-11-09",{"path":2523,"title":2524,"description":2525,"date":2526,"series":2468,"seriesOrder":421},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-17","软件分析与设计笔记 17：用生成式 AI 做设计与开发：能用的场景与要防的坑","生成式 AI 在工作流里真正站得住的三个场景 —— 造示例数据、解释陌生代码、起草初稿；它为什么会复现社会偏见、为什么会编得像真的，以及哪些内容绝不能直接采用。","2026-11-08",{"path":2528,"title":2529,"description":2530,"date":2531,"series":2468,"seriesOrder":416},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-16","软件分析与设计笔记 16：从 persona 到 URS：一份交付物的完整骨架","把前面十几篇串成一份能交出去的东西：用户需求规格说明书的九个部分、每一章来自哪个环节、由谁确认，以及面向决策者的提案该讲清哪三件事。","2026-11-07",{"path":2533,"title":2534,"description":2535,"date":2536,"series":2468,"seriesOrder":410},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-15","软件分析与设计笔记 15：非功能需求：把「快、稳、安全」变成数字","为什么「系统要快」不是一条需求：非功能需求怎么量化，决定性能的用户量、交易量、数据量三张表怎么估，安全需求要写清什么，以及可靠性的「不可用」该怎么定义。","2026-11-06",{"path":2538,"title":2539,"description":2540,"date":2541,"series":2468,"seriesOrder":395},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-14","软件分析与设计笔记 14：软件测试：层级、方法与等价类划分","「我们测过了」这句话问题在哪：为什么静态评审和动态测试缺一不可，五个测试层级各自验证什么，白盒与黑盒的差别，以及用等价类划分和边界值把无限输入变成有限用例的方法。","2026-11-05",{"path":2543,"title":2544,"description":2545,"date":2546,"series":2468,"seriesOrder":383},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-13","软件分析与设计笔记 13：需求获取：五种调查方法与一场访谈","需求从哪里来：五种事实调查方法各自适合回答什么、代价是什么，访谈从准备到记录的全过程，问卷设计里最容易犯的错，以及访谈记录必须写下来的那几项信息。","2026-11-04",{"path":2548,"title":2549,"description":2550,"date":2551,"series":2468,"seriesOrder":377},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-12","软件分析与设计笔记 12：前端设计：把流程变成屏幕","界面设计不是「画得好看」：从活动图走到屏幕的三步，CRUD 任务流怎么当检查表，界面必须回答的三个问题，信息架构与五种导航类型，视觉一致性与留白，以及移动端与 Web 端的差别。","2026-11-03",{"path":2553,"title":2554,"description":2555,"date":2556,"series":2468,"seriesOrder":372},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-11","软件分析与设计笔记 11：Agentic AI 与业务流程自动化","AI agent 到底是什么：五项能力与四个核心组件，什么场景该用定时任务、什么该用程序、什么才值得用 agent，两种实现路径的差别，必须提前防住的四类失败模式，以及设计方法从提示词到编排的演进。","2026-11-02",{"path":2558,"title":2559,"description":2560,"date":2561,"series":2468,"seriesOrder":366},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-10","软件分析与设计笔记 10：精炼用例模型：include、extend 与泛化","三种精炼用例关系的判断标准：include 什么时候用、它和 extend 的区别到底在哪、参与者泛化与用例泛化各解决什么问题，以及精炼到什么程度就该停手。","2026-11-01",{"path":2563,"title":2564,"description":2565,"date":2566,"series":2468,"seriesOrder":349},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-09","软件分析与设计笔记 09：功能需求（二）：从流程到业务实体","活动图画完之后，怎么从流程里筛出真正的业务实体：三条判断标准、哪些名词其实不该建实体、属性从哪里来，以及领域模型和后面的数据模型分别负责什么。","2026-10-31",{"path":2568,"title":2569,"description":2570,"date":2571,"series":2468,"seriesOrder":333},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-08","软件分析与设计笔记 08：功能需求（一）：用例模型与活动图","从用户故事到用例的翻译过程：什么才算「好」的参与者，系统边界怎么划，用例描述必须写清哪四件事，活动图怎么把分支、并行与泳道画清楚，以及这两个图在 draw.io 里各用哪个形状、最容易画错哪一笔。","2026-10-30",{"path":2573,"title":2574,"description":2575,"date":2576,"series":2468,"seriesOrder":324},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-07","软件分析与设计笔记 07：用户故事：把需求写成可验收的一句话","用户故事的三段结构，怎么从用户画像和数据地图里把它提取出来，一条故事多大算合适、时序为什么也是需求的一部分，以及验收标准怎么写才算真的可验收。","2026-10-29",{"path":2578,"title":2579,"description":2580,"date":2581,"series":2468,"seriesOrder":318},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-06","软件分析与设计笔记 06：范围界定（数据侧）：用数据校验你的判断","访谈结论和数字对不上时该怎么办：分析的四个层次分别在回答什么问题、为什么不能跳级、数据准备与搜集怎么做成一张应用地图，以及数据最容易在哪几个地方骗你。","2026-10-28",{"path":2583,"title":2584,"description":2585,"date":2586,"series":2468,"seriesOrder":311},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-05","软件分析与设计笔记 05：范围界定（用户侧）：用户画像与客户旅程图","为什么「所有用户」是一个陷阱，项目边界该怎么划，用户画像要写哪五个要素、最容易写成什么废纸，客户旅程图怎么把阶段、想法和情绪画在同一张纸上，以及怎么从这两样东西一步步推到可验证的需求。","2026-10-27",{"path":2588,"title":2589,"description":2590,"date":2591,"series":2468,"seriesOrder":305},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-04","软件分析与设计笔记 04：接手陌生案例的第一步：三问与一张表","拿到一个完全陌生的业务案例，怎么从零开始不跑偏：只问 Who、What、Why 三个问题，把「问题—事实—假设—理由」记成一张四列表，说清为什么假设必须写明理由，以及带着问题进访谈为什么比空手去有效得多。","2026-10-26",{"path":2593,"title":2594,"description":2595,"date":2596,"series":2468,"seriesOrder":281},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-03","软件分析与设计笔记 03：Agile 与 Scrum：迭代、角色与产出物","从「三周做完交付，客户说这不是我要的」讲起，说清瀑布式与迭代式的根本差别，Scrum 的三个角色和三种产出物，一个 sprint 里四次固定活动各自在解决什么问题，以及敏捷里最常见的几个误解。","2026-10-25",{"path":2598,"title":2599,"description":2600,"date":2601,"series":2468,"seriesOrder":270},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-02","软件分析与设计笔记 02：设计思维、用户驱动与数据驱动","从「客服要一个能一次看完所有排期的界面」这个翻车案例讲起，说清设计思维到底是什么、用户驱动与数据驱动各自能看见什么又看不见什么、为什么真正的落点是把两者合起来的 data-informed design，以及访谈中怎么问出有用的信息。","2026-10-24",{"path":2603,"title":2604,"description":2605,"date":2606,"series":2468,"seriesOrder":263},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-01","软件分析与设计笔记 01：课程地图与从需求到设计的主干","这一篇不写具体方法，先把整条链路铺开：软件分析与设计到底要解决什么问题，从需求到设计分成哪两段，中间有哪十几个环节，一份完整的交付物里应该有什么，以及后面 27 篇笔记怎么排、新人该按什么顺序读。","2026-10-23",{"path":2608,"title":2609,"description":2610,"date":2611,"series":2612,"seriesOrder":263},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-01","Spring 学习笔记 01：IoC、依赖注入与第一个 Spring Boot 应用","从「手写 new 出来的三层结构」的痛点讲起，说清 IoC 容器、Bean 与依赖注入是怎么回事，再用 Maven 与 Spring Initializr 跑起第一个能返回 JSON 的应用。","2026-10-15","Spring 学习笔记",{"path":2614,"title":2615,"description":2616,"date":2611,"series":2612,"seriesOrder":270},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-02","Spring 学习笔记 02：Spring Data JPA 基础——实体映射与 Repository","用 ORM 把类映射到表：@Entity、@Id、@GeneratedValue 怎么用，CrudRepository 三层接口怎么选，H2 与 MySQL 怎么配，以及 ddl-auto 到底该写哪个值。",{"path":2618,"title":2619,"description":2620,"date":2611,"series":2612,"seriesOrder":281},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-03","Spring 学习笔记 03：JPA 查询——派生方法、JPQL 与分页排序","Repository 自带的方法只够按主键查，这篇讲三种自己造查询的方式：方法名派生、@Query 写 JPQL、原生 SQL，以及 Pageable 分页排序和投影返回。",{"path":2622,"title":2623,"description":2624,"date":2611,"series":2612,"seriesOrder":305},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-04","Spring 学习笔记 04：JPA 关联映射与仓储测试","一对多、多对多怎么映射，谁负责维护外键，FetchType 与 CascadeType 该怎么选，顺带用 @DataJpaTest 与 AssertJ 把仓储层测起来。",{"path":2626,"title":2627,"description":2628,"date":2611,"series":2612,"seriesOrder":311},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-05","Spring 学习笔记 05：Spring MVC 与 Thymeleaf 入门","一次页面请求在 Spring MVC 里走了哪几步，@Controller 怎么把数据交给模板，@PathVariable 与 @RequestParam 怎么取参数，以及控制器为什么必须是无状态的。",{"path":2630,"title":2631,"description":2632,"date":2611,"series":2612,"seriesOrder":318},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-06","Spring 学习笔记 06：表单绑定与服务端校验","把表单字段绑到对象上，用 Bean Validation 注解做服务端校验，把错误信息显示回用户刚填的那一页，再补上自定义校验注解与全局异常处理。",{"path":2634,"title":2635,"description":2636,"date":2611,"series":2612,"seriesOrder":324},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-07","Spring 学习笔记 07：Thymeleaf 片段复用与国际化","用 th:fragment 与 th:insert\u002Fth:replace 把导航栏、页脚抽成片段，页面套用统一布局，再用 messages.properties 与 LocaleResolver 做中英文切换。",{"path":2638,"title":2639,"description":2640,"date":2611,"series":2612,"seriesOrder":333},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-08","Spring 学习笔记 08：HTTP Session 与会话状态管理","HTTP 无状态带来的问题，Cookie 与 Session 怎么分工，Spring 里读写会话的几种方式，以及会话固定、超时、多实例共享这些必须想清楚的事。",{"path":2642,"title":2643,"description":2644,"date":2611,"series":2612,"seriesOrder":349},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-09","Spring 学习笔记 09：拦截器 HandlerInterceptor","登录检查、权限判断、请求耗时统计这类横切逻辑该放哪：HandlerInterceptor 的三个回调各在什么时机执行，怎么注册与排除路径，以及它和过滤器的分工。",{"path":2646,"title":2647,"description":2648,"date":2611,"series":2612,"seriesOrder":366},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-10","Spring 学习笔记 10：Service 层与事务","一组数据库操作要么全成功要么全失败，靠 @Transactional 怎么做到；传播行为、隔离级别、回滚规则怎么选，以及自调用失效、乐观锁悲观锁这些实战里的坑。",{"path":2650,"title":2651,"description":2652,"date":2611,"series":2612,"seriesOrder":372},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-11","Spring 学习笔记 11：REST API 基础——把 Service 暴露成接口","同一套 Service 怎么同时服务页面和客户端：@RestController 的路由与方法语义、状态码怎么给、DTO 怎么定，以及 MockMvc 端到端和切片测试的两种写法与各自的坑。",{"path":2654,"title":2655,"description":2656,"date":2611,"series":2612,"seriesOrder":377},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-12","Spring 学习笔记 12：REST 进阶——版本、更新、跨域与文档化","接口能跑通之后要处理的几件事：URL 与响应结构怎么定、版本怎么演进、更新怎么做到可安全重试、CORS 怎么配、HATEOAS 与 Spring Data REST 该不该用。",{"path":2658,"title":2659,"description":2660,"date":2661,"series":2662,"seriesOrder":305},"\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes","Java 函数式编程笔记：Lambda、函数式接口与 Streams","学习 Java 函数式编程时的个人笔记：Lambda 表达式的五步简写与方法引用、常见函数式接口、Stream 流水线与惰性求值，以及创建流、中间操作、终止操作与收集器的用法。示例代码均为自写，运行结果来自本地实测。","2026-10-13","Java 学习笔记",{"path":2664,"title":2665,"description":2666,"date":2661,"series":2662,"seriesOrder":281},"\u002Fposts\u002Fjava\u002Fjava-generics-notes","Java 泛型笔记：泛型方法、有界类型与泛型类","学习 Java 泛型时的个人笔记：从重复的重载方法与多态尝试讲起，再到泛型方法与类型推断、有界泛型类型、Comparable 与 Number 边界，以及泛型类在集合中的用法。示例代码均为自写，运行结果来自本地实测。",{"path":2668,"title":2669,"description":2670,"date":2448,"series":2455,"seriesOrder":305},"\u002Fposts\u002Fmysql\u002Fmysql-learning-04","MySQL 学习笔记 04：数据操纵（增删改）","MySQL 学习笔记第四篇：INSERT 插入（含批量插入与 INSERT ... SELECT）、UPDATE 更新、DELETE 删除，以及各自的坑。",{"path":2672,"title":2673,"description":2674,"date":2448,"series":2455,"seriesOrder":311},"\u002Fposts\u002Fmysql\u002Fmysql-learning-05","MySQL 学习笔记 05：DDL 概览","MySQL 学习笔记第五篇：数据定义语言概览——Schema、Table、View、Index、Constraints 这几类对象分别对应哪些 CREATE \u002F ALTER \u002F DROP 命令。",{"path":2676,"title":2677,"description":2678,"date":2448,"series":2455,"seriesOrder":318},"\u002Fposts\u002Fmysql\u002Fmysql-learning-06","MySQL 学习笔记 06：CREATE TABLE 与数据类型","MySQL 学习笔记第六篇：CREATE TABLE 的完整语法、常用数据类型的选型建议，以及外键的两种定义方式。",{"path":2680,"title":2681,"description":2682,"date":2448,"series":2455,"seriesOrder":324},"\u002Fposts\u002Fmysql\u002Fmysql-learning-07","MySQL 学习笔记 07：索引","MySQL 学习笔记第七篇：索引的作用与 B+ 树结构、单列与复合索引的创建、索引的代价与适用场景。",{"path":2684,"title":2685,"description":2686,"date":2448,"series":2455,"seriesOrder":333},"\u002Fposts\u002Fmysql\u002Fmysql-learning-08","MySQL 学习笔记 08：DROP 与 ALTER","MySQL 学习笔记第八篇：DROP 删除数据库对象、ALTER 修改表结构，以及常见的坑。",{"path":2453,"title":5,"description":2449,"date":2448,"series":2455,"seriesOrder":349},{"path":2689,"title":2690,"description":2691,"date":2448,"series":2455,"seriesOrder":366},"\u002Fposts\u002Fmysql\u002Fmysql-learning-10","MySQL 学习笔记 10：视图","MySQL 学习笔记第十篇：视图（Views）的定义与特性、和表的对比、创建与使用，以及可更新视图的限制。",{"path":2693,"title":2694,"description":2695,"date":2448,"series":2455,"seriesOrder":372},"\u002Fposts\u002Fmysql\u002Fmysql-learning-11","MySQL 学习笔记 11：存储过程","MySQL 学习笔记第十一篇：存储过程（Stored Procedures）的概念、创建与调用、带参数的写法，以及优缺点。",{"path":2697,"title":2698,"description":2699,"date":2700,"series":2455,"seriesOrder":263},"\u002Fposts\u002Fmysql\u002Fmysql-learning-01","MySQL 学习笔记 01：MySQL 基础知识","MySQL 学习笔记第一篇：MySQL Server 与 Workbench 的安装，数据库与 DBMS 的基本概念，关系模型、ACID 事务、常见数据类型，以及后续的学习路线。","2026-10-09",{"path":2702,"title":2703,"description":2704,"date":2700,"series":2455,"seriesOrder":270},"\u002Fposts\u002Fmysql\u002Fmysql-learning-02","MySQL 学习笔记 02：SELECT 查询与过滤","MySQL 学习笔记第二篇：SELECT 的基本语法与逻辑执行顺序，WHERE 过滤（比较、LIKE、IN、BETWEEN、NULL）、各种 JOIN、UNION 与子查询。",{"path":2706,"title":2707,"description":2708,"date":2700,"series":2455,"seriesOrder":281},"\u002Fposts\u002Fmysql\u002Fmysql-learning-03","MySQL 学习笔记 03：聚合、分组与输出控制","MySQL 学习笔记第三篇：COUNT\u002FSUM\u002FAVG 等聚合函数、GROUP BY 分组、HAVING 过滤、ORDER BY 与 LIMIT，以及一条 SELECT 的完整逻辑执行顺序。",{"path":2710,"title":2711,"description":2712,"date":2713,"series":2662,"seriesOrder":270},"\u002Fposts\u002Fjava\u002Fjava-oop-notes","Java 面向对象笔记：类、继承、多态与异常","学习 Java 面向对象编程时的个人笔记：类与对象、封装与访问修饰符、继承与多态、集合、抽象类与接口、异常处理。示例代码均为自写，运行结果来自本地实测。","2026-10-06",{"path":2715,"title":2716,"description":2717,"date":2718,"series":2662,"seriesOrder":263},"\u002Fposts\u002Fjava\u002Fjava-basics-notes","Java 基础语法笔记：从环境安装到方法与数组","学习 Java 基础语法时的个人笔记：JDK 安装、输入输出、变量与运算符、条件与循环、字符串、数组与方法。示例代码均为自写，运行结果来自本地实测。","2026-10-05",{"path":2720,"title":2721,"description":2722,"date":2723,"series":2724,"seriesOrder":263},"\u002Fposts\u002Fgit\u002Fgit-install-and-basics","Git 安装与基本使用","从装好 Git 到跑通日常提交流程，把最常用的几条命令和它们背后的三个区域讲清楚。","2026-10-04","Git 入门",{"path":2726,"title":2727,"description":2728,"date":2723,"series":2724,"seriesOrder":270},"\u002Fposts\u002Fgit\u002Flocal-remote-repository","本地远程仓库的部署","用自己的机器当 Git 服务器：建一个裸仓库、走 SSH 连上去，把本地提交推上去。",{"path":2730,"title":2731,"description":2732,"date":2723,"series":2733,"seriesOrder":263},"\u002Fposts\u002Fguide\u002Fguide-file-layout","文件都放在哪","文章、图片、封面分别放在仓库的什么位置，哪些目录可以放手改、哪些碰了会出问题。","博客使用指南",{"path":2735,"title":2736,"description":2737,"date":2723,"series":2733,"seriesOrder":270},"\u002Fposts\u002Fguide\u002Fguide-frontmatter","frontmatter 每个字段是什么意思","文章开头那段被横线包住的字段，逐个说明作用、默认行为和写错的后果。",{"path":2739,"title":2740,"description":2741,"date":2723,"series":2733,"seriesOrder":281},"\u002Fposts\u002Fguide\u002Fguide-markdown","正文怎么写 —— Markdown 与排版效果","标题、链接、图片、代码、表格分别怎么写，以及本站对它们做了哪些自定义处理。",1793268186298]