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