[{"data":1,"prerenderedAt":1267},["ShallowReactive",2],{"blog-\u002Fblog\u002Fsql-views-virtual-tables":3},{"id":4,"title":5,"body":6,"description":1253,"difficulty":1254,"extension":1255,"framework":1256,"frameworkSlug":55,"meta":1257,"navigation":226,"order":70,"path":1258,"qaPath":1259,"seo":1260,"stem":1261,"subtopic":1262,"topic":1263,"topicSlug":1264,"updated":1265,"__hash__":1266},"blog\u002Fblog\u002Fsql-views-virtual-tables.md","SQL Views — Simplifying Queries, Controlling Access, and Materialized Views",{"type":7,"value":8,"toc":1243},"minimark",[9,14,31,46,50,325,331,335,338,440,443,488,492,513,653,660,664,670,973,977,1077,1081,1218,1222,1239],[10,11,13],"h2",{"id":12},"what-a-view-is","What a view is",[15,16,17,18,22,23,27,28,30],"p",{},"A ",[19,20,21],"strong",{},"view"," is a named ",[24,25,26],"code",{},"SELECT"," statement stored in the database. It looks and\nbehaves like a table from the caller's perspective but contains no data of its\nown — every query against a view re-executes the underlying ",[24,29,26],{}," against the\nbase tables (unless it is a materialized view).",[15,32,33,34,37,38,41,42,45],{},"Views serve three purposes: ",[19,35,36],{},"simplify"," complex queries into a reusable name,\n",[19,39,40],{},"control access"," by exposing only certain columns or rows, and ",[19,43,44],{},"decouple","\napplication code from the underlying schema.",[10,47,49],{"id":48},"creating-and-using-views","Creating and using views",[51,52,57],"pre",{"className":53,"code":54,"language":55,"meta":56,"style":56},"language-sql shiki shiki-themes github-light github-dark","-- A view that joins orders with customer details\nCREATE OR REPLACE VIEW order_summary AS\nSELECT\n    o.id                    AS order_id,\n    c.email                 AS customer_email,\n    c.display_name          AS customer_name,\n    o.status,\n    o.total_amount,\n    o.created_at\nFROM orders    o\nJOIN customers c ON c.id = o.customer_id;\n\n-- Use it like a table\nSELECT * FROM order_summary WHERE status = 'pending' ORDER BY created_at DESC;\nSELECT customer_email, COUNT(*) AS orders, SUM(total_amount) AS revenue\nFROM   order_summary\nGROUP  BY customer_email;\n","sql","",[24,58,59,68,85,91,111,128,144,157,169,179,188,221,228,234,271,308,316],{"__ignoreMap":56},[60,61,64],"span",{"class":62,"line":63},"line",1,[60,65,67],{"class":66},"sJ8bj","-- A view that joins orders with customer details\n",[60,69,71,75,78,82],{"class":62,"line":70},2,[60,72,74],{"class":73},"szBVR","CREATE OR REPLACE",[60,76,77],{"class":73}," VIEW",[60,79,81],{"class":80},"sScJk"," order_summary",[60,83,84],{"class":73}," AS\n",[60,86,88],{"class":62,"line":87},3,[60,89,90],{"class":73},"SELECT\n",[60,92,94,98,102,105,108],{"class":62,"line":93},4,[60,95,97],{"class":96},"sj4cs","    o",[60,99,101],{"class":100},"sVt8B",".",[60,103,104],{"class":96},"id",[60,106,107],{"class":73},"                    AS",[60,109,110],{"class":100}," order_id,\n",[60,112,114,117,119,122,125],{"class":62,"line":113},5,[60,115,116],{"class":96},"    c",[60,118,101],{"class":100},[60,120,121],{"class":96},"email",[60,123,124],{"class":73},"                 AS",[60,126,127],{"class":100}," customer_email,\n",[60,129,131,133,135,138,141],{"class":62,"line":130},6,[60,132,116],{"class":96},[60,134,101],{"class":100},[60,136,137],{"class":96},"display_name",[60,139,140],{"class":73},"          AS",[60,142,143],{"class":100}," customer_name,\n",[60,145,147,149,151,154],{"class":62,"line":146},7,[60,148,97],{"class":96},[60,150,101],{"class":100},[60,152,153],{"class":96},"status",[60,155,156],{"class":100},",\n",[60,158,160,162,164,167],{"class":62,"line":159},8,[60,161,97],{"class":96},[60,163,101],{"class":100},[60,165,166],{"class":96},"total_amount",[60,168,156],{"class":100},[60,170,172,174,176],{"class":62,"line":171},9,[60,173,97],{"class":96},[60,175,101],{"class":100},[60,177,178],{"class":96},"created_at\n",[60,180,182,185],{"class":62,"line":181},10,[60,183,184],{"class":73},"FROM",[60,186,187],{"class":100}," orders    o\n",[60,189,191,194,197,200,203,205,207,210,213,215,218],{"class":62,"line":190},11,[60,192,193],{"class":73},"JOIN",[60,195,196],{"class":100}," customers c ",[60,198,199],{"class":73},"ON",[60,201,202],{"class":96}," c",[60,204,101],{"class":100},[60,206,104],{"class":96},[60,208,209],{"class":73}," =",[60,211,212],{"class":96}," o",[60,214,101],{"class":100},[60,216,217],{"class":96},"customer_id",[60,219,220],{"class":100},";\n",[60,222,224],{"class":62,"line":223},12,[60,225,227],{"emptyLinePlaceholder":226},true,"\n",[60,229,231],{"class":62,"line":230},13,[60,232,233],{"class":66},"-- Use it like a table\n",[60,235,237,239,242,245,248,251,254,256,260,263,266,269],{"class":62,"line":236},14,[60,238,26],{"class":73},[60,240,241],{"class":73}," *",[60,243,244],{"class":73}," FROM",[60,246,247],{"class":100}," order_summary ",[60,249,250],{"class":73},"WHERE",[60,252,253],{"class":73}," status",[60,255,209],{"class":73},[60,257,259],{"class":258},"sZZnC"," 'pending'",[60,261,262],{"class":73}," ORDER BY",[60,264,265],{"class":100}," created_at ",[60,267,268],{"class":73},"DESC",[60,270,220],{"class":100},[60,272,274,276,279,282,285,288,291,294,297,300,303,305],{"class":62,"line":273},15,[60,275,26],{"class":73},[60,277,278],{"class":100}," customer_email, ",[60,280,281],{"class":96},"COUNT",[60,283,284],{"class":100},"(",[60,286,287],{"class":73},"*",[60,289,290],{"class":100},") ",[60,292,293],{"class":73},"AS",[60,295,296],{"class":100}," orders, ",[60,298,299],{"class":96},"SUM",[60,301,302],{"class":100},"(total_amount) ",[60,304,293],{"class":73},[60,306,307],{"class":100}," revenue\n",[60,309,311,313],{"class":62,"line":310},16,[60,312,184],{"class":73},[60,314,315],{"class":100},"   order_summary\n",[60,317,319,322],{"class":62,"line":318},17,[60,320,321],{"class":73},"GROUP  BY",[60,323,324],{"class":100}," customer_email;\n",[15,326,327,330],{},[24,328,329],{},"CREATE OR REPLACE VIEW"," updates the view definition without dropping it, so\nexisting permissions on the view are preserved.",[10,332,334],{"id":333},"views-for-access-control","Views for access control",[15,336,337],{},"Grant access to a view but not the underlying tables to restrict what users can\nsee:",[51,339,341],{"className":53,"code":340,"language":55,"meta":56,"style":56},"-- Base table has sensitive columns (salary, SSN)\n-- View exposes only what HR partners need\nCREATE VIEW employee_directory AS\nSELECT id, full_name, department, job_title, hire_date\nFROM   employees\nWHERE  terminated_at IS NULL;\n\nGRANT SELECT ON employee_directory TO hr_partner_role;\nREVOKE ALL ON employees FROM hr_partner_role;\n-- HR partners can query employee_directory but not employees directly\n",[24,342,343,348,353,365,372,379,394,398,418,435],{"__ignoreMap":56},[60,344,345],{"class":62,"line":63},[60,346,347],{"class":66},"-- Base table has sensitive columns (salary, SSN)\n",[60,349,350],{"class":62,"line":70},[60,351,352],{"class":66},"-- View exposes only what HR partners need\n",[60,354,355,358,360,363],{"class":62,"line":87},[60,356,357],{"class":73},"CREATE",[60,359,77],{"class":73},[60,361,362],{"class":80}," employee_directory",[60,364,84],{"class":73},[60,366,367,369],{"class":62,"line":93},[60,368,26],{"class":73},[60,370,371],{"class":100}," id, full_name, department, job_title, hire_date\n",[60,373,374,376],{"class":62,"line":113},[60,375,184],{"class":73},[60,377,378],{"class":100},"   employees\n",[60,380,381,383,386,389,392],{"class":62,"line":130},[60,382,250],{"class":73},[60,384,385],{"class":100},"  terminated_at ",[60,387,388],{"class":73},"IS",[60,390,391],{"class":73}," NULL",[60,393,220],{"class":100},[60,395,396],{"class":62,"line":146},[60,397,227],{"emptyLinePlaceholder":226},[60,399,400,403,406,409,412,415],{"class":62,"line":159},[60,401,402],{"class":73},"GRANT",[60,404,405],{"class":73}," SELECT",[60,407,408],{"class":73}," ON",[60,410,411],{"class":100}," employee_directory ",[60,413,414],{"class":73},"TO",[60,416,417],{"class":100}," hr_partner_role;\n",[60,419,420,423,426,428,431,433],{"class":62,"line":171},[60,421,422],{"class":73},"REVOKE",[60,424,425],{"class":100}," ALL ",[60,427,199],{"class":73},[60,429,430],{"class":100}," employees ",[60,432,184],{"class":73},[60,434,417],{"class":100},[60,436,437],{"class":62,"line":181},[60,438,439],{"class":66},"-- HR partners can query employee_directory but not employees directly\n",[15,441,442],{},"Row-level filtering in views is an alternative to row-level security policies:",[51,444,446],{"className":53,"code":445,"language":55,"meta":56,"style":56},"-- Each sales rep can only see their own assigned leads\nCREATE VIEW my_leads AS\nSELECT * FROM leads\nWHERE assigned_to = current_user;\n",[24,447,448,453,464,475],{"__ignoreMap":56},[60,449,450],{"class":62,"line":63},[60,451,452],{"class":66},"-- Each sales rep can only see their own assigned leads\n",[60,454,455,457,459,462],{"class":62,"line":70},[60,456,357],{"class":73},[60,458,77],{"class":73},[60,460,461],{"class":80}," my_leads",[60,463,84],{"class":73},[60,465,466,468,470,472],{"class":62,"line":87},[60,467,26],{"class":73},[60,469,241],{"class":73},[60,471,244],{"class":73},[60,473,474],{"class":100}," leads\n",[60,476,477,479,482,485],{"class":62,"line":93},[60,478,250],{"class":73},[60,480,481],{"class":100}," assigned_to ",[60,483,484],{"class":73},"=",[60,486,487],{"class":100}," current_user;\n",[10,489,491],{"id":490},"updatable-views","Updatable views",[15,493,494,495,498,499,502,503,156,506,502,509,512],{},"A view is ",[19,496,497],{},"updatable"," (INSERT\u002FUPDATE\u002FDELETE pass through to the base table) if\nit meets specific criteria: single base table, no ",[24,500,501],{},"DISTINCT",", ",[24,504,505],{},"GROUP BY",[24,507,508],{},"HAVING",[24,510,511],{},"UNION",", aggregate functions, or window functions.",[51,514,516],{"className":53,"code":515,"language":55,"meta":56,"style":56},"-- Simple updatable view\nCREATE VIEW active_products AS\nSELECT id, product_name, unit_price, stock_qty\nFROM   products\nWHERE  status = 'active';\n\n-- This INSERT goes to the products table\nINSERT INTO active_products (product_name, unit_price, stock_qty)\nVALUES ('USB-C Hub', 29.99, 500);\n\n-- This UPDATE modifies the base table row\nUPDATE active_products SET unit_price = 34.99 WHERE id = 77;\n",[24,517,518,523,534,541,548,562,566,571,579,608,612,617],{"__ignoreMap":56},[60,519,520],{"class":62,"line":63},[60,521,522],{"class":66},"-- Simple updatable view\n",[60,524,525,527,529,532],{"class":62,"line":70},[60,526,357],{"class":73},[60,528,77],{"class":73},[60,530,531],{"class":80}," active_products",[60,533,84],{"class":73},[60,535,536,538],{"class":62,"line":87},[60,537,26],{"class":73},[60,539,540],{"class":100}," id, product_name, unit_price, stock_qty\n",[60,542,543,545],{"class":62,"line":93},[60,544,184],{"class":73},[60,546,547],{"class":100},"   products\n",[60,549,550,552,555,557,560],{"class":62,"line":113},[60,551,250],{"class":73},[60,553,554],{"class":73},"  status",[60,556,209],{"class":73},[60,558,559],{"class":258}," 'active'",[60,561,220],{"class":100},[60,563,564],{"class":62,"line":130},[60,565,227],{"emptyLinePlaceholder":226},[60,567,568],{"class":62,"line":146},[60,569,570],{"class":66},"-- This INSERT goes to the products table\n",[60,572,573,576],{"class":62,"line":159},[60,574,575],{"class":73},"INSERT INTO",[60,577,578],{"class":100}," active_products (product_name, unit_price, stock_qty)\n",[60,580,581,584,587,590,592,595,597,600,602,605],{"class":62,"line":171},[60,582,583],{"class":73},"VALUES",[60,585,586],{"class":100}," (",[60,588,589],{"class":258},"'USB-C Hub'",[60,591,502],{"class":100},[60,593,594],{"class":96},"29",[60,596,101],{"class":100},[60,598,599],{"class":96},"99",[60,601,502],{"class":100},[60,603,604],{"class":96},"500",[60,606,607],{"class":100},");\n",[60,609,610],{"class":62,"line":181},[60,611,227],{"emptyLinePlaceholder":226},[60,613,614],{"class":62,"line":190},[60,615,616],{"class":66},"-- This UPDATE modifies the base table row\n",[60,618,619,622,625,628,631,633,636,638,640,643,646,648,651],{"class":62,"line":223},[60,620,621],{"class":73},"UPDATE",[60,623,624],{"class":100}," active_products ",[60,626,627],{"class":73},"SET",[60,629,630],{"class":100}," unit_price ",[60,632,484],{"class":73},[60,634,635],{"class":96}," 34",[60,637,101],{"class":100},[60,639,599],{"class":96},[60,641,642],{"class":73}," WHERE",[60,644,645],{"class":100}," id ",[60,647,484],{"class":73},[60,649,650],{"class":96}," 77",[60,652,220],{"class":100},[15,654,655,656,659],{},"For complex views that cannot be updatable automatically, use ",[24,657,658],{},"INSTEAD OF","\ntriggers (SQL Server) or rewrite rules (Postgres) to define custom write\nbehaviour.",[10,661,663],{"id":662},"materialized-views-cached-query-results","Materialized views — cached query results",[15,665,17,666,669],{},[19,667,668],{},"materialized view"," stores the result set physically. Queries against it\ndo not re-execute the underlying SELECT — they read the cached data. This is a\nmajor performance win for expensive aggregations and joins.",[51,671,673],{"className":53,"code":672,"language":55,"meta":56,"style":56},"-- Postgres: create a materialized view of the daily sales summary\nCREATE MATERIALIZED VIEW daily_sales_summary AS\nSELECT\n    DATE(o.created_at)               AS sale_date,\n    p.category,\n    COUNT(DISTINCT o.id)             AS order_count,\n    SUM(oi.quantity * oi.unit_price) AS revenue\nFROM orders      o\nJOIN order_items oi ON oi.order_id = o.id\nJOIN products    p  ON p.id = oi.product_id\nWHERE o.status = 'completed'\nGROUP BY DATE(o.created_at), p.category\nWITH DATA;  -- populate immediately (use WITH NO DATA to defer)\n\n-- Query the cached result (no re-execution of the underlying query)\nSELECT * FROM daily_sales_summary WHERE sale_date >= '2026-06-01';\n\n-- Refresh when source data changes\nREFRESH MATERIALIZED VIEW daily_sales_summary;\n\n-- Refresh without locking reads (Postgres, requires a UNIQUE index)\nCREATE UNIQUE INDEX ON daily_sales_summary (sale_date, category);\nREFRESH MATERIALIZED VIEW CONCURRENTLY daily_sales_summary;\n",[24,674,675,680,690,694,717,729,752,783,790,815,840,855,880,894,898,903,927,931,937,943,948,954,967],{"__ignoreMap":56},[60,676,677],{"class":62,"line":63},[60,678,679],{"class":66},"-- Postgres: create a materialized view of the daily sales summary\n",[60,681,682,684,687],{"class":62,"line":70},[60,683,357],{"class":73},[60,685,686],{"class":100}," MATERIALIZED VIEW daily_sales_summary ",[60,688,689],{"class":73},"AS\n",[60,691,692],{"class":62,"line":87},[60,693,90],{"class":73},[60,695,696,699,701,704,706,709,712,714],{"class":62,"line":93},[60,697,698],{"class":73},"    DATE",[60,700,284],{"class":100},[60,702,703],{"class":96},"o",[60,705,101],{"class":100},[60,707,708],{"class":96},"created_at",[60,710,711],{"class":100},")               ",[60,713,293],{"class":73},[60,715,716],{"class":100}," sale_date,\n",[60,718,719,722,724,727],{"class":62,"line":113},[60,720,721],{"class":96},"    p",[60,723,101],{"class":100},[60,725,726],{"class":96},"category",[60,728,156],{"class":100},[60,730,731,734,736,738,740,742,744,747,749],{"class":62,"line":130},[60,732,733],{"class":96},"    COUNT",[60,735,284],{"class":100},[60,737,501],{"class":73},[60,739,212],{"class":96},[60,741,101],{"class":100},[60,743,104],{"class":96},[60,745,746],{"class":100},")             ",[60,748,293],{"class":73},[60,750,751],{"class":100}," order_count,\n",[60,753,754,757,759,762,764,767,769,772,774,777,779,781],{"class":62,"line":146},[60,755,756],{"class":96},"    SUM",[60,758,284],{"class":100},[60,760,761],{"class":96},"oi",[60,763,101],{"class":100},[60,765,766],{"class":96},"quantity",[60,768,241],{"class":73},[60,770,771],{"class":96}," oi",[60,773,101],{"class":100},[60,775,776],{"class":96},"unit_price",[60,778,290],{"class":100},[60,780,293],{"class":73},[60,782,307],{"class":100},[60,784,785,787],{"class":62,"line":159},[60,786,184],{"class":73},[60,788,789],{"class":100}," orders      o\n",[60,791,792,794,797,799,801,803,806,808,810,812],{"class":62,"line":171},[60,793,193],{"class":73},[60,795,796],{"class":100}," order_items oi ",[60,798,199],{"class":73},[60,800,771],{"class":96},[60,802,101],{"class":100},[60,804,805],{"class":96},"order_id",[60,807,209],{"class":73},[60,809,212],{"class":96},[60,811,101],{"class":100},[60,813,814],{"class":96},"id\n",[60,816,817,819,822,824,827,829,831,833,835,837],{"class":62,"line":181},[60,818,193],{"class":73},[60,820,821],{"class":100}," products    p  ",[60,823,199],{"class":73},[60,825,826],{"class":96}," p",[60,828,101],{"class":100},[60,830,104],{"class":96},[60,832,209],{"class":73},[60,834,771],{"class":96},[60,836,101],{"class":100},[60,838,839],{"class":96},"product_id\n",[60,841,842,844,846,848,850,852],{"class":62,"line":190},[60,843,250],{"class":73},[60,845,212],{"class":96},[60,847,101],{"class":100},[60,849,153],{"class":96},[60,851,209],{"class":73},[60,853,854],{"class":258}," 'completed'\n",[60,856,857,859,862,864,866,868,870,873,875,877],{"class":62,"line":223},[60,858,505],{"class":73},[60,860,861],{"class":73}," DATE",[60,863,284],{"class":100},[60,865,703],{"class":96},[60,867,101],{"class":100},[60,869,708],{"class":96},[60,871,872],{"class":100},"), ",[60,874,15],{"class":96},[60,876,101],{"class":100},[60,878,879],{"class":96},"category\n",[60,881,882,885,888,891],{"class":62,"line":230},[60,883,884],{"class":73},"WITH",[60,886,887],{"class":73}," DATA",[60,889,890],{"class":100},";  ",[60,892,893],{"class":66},"-- populate immediately (use WITH NO DATA to defer)\n",[60,895,896],{"class":62,"line":236},[60,897,227],{"emptyLinePlaceholder":226},[60,899,900],{"class":62,"line":273},[60,901,902],{"class":66},"-- Query the cached result (no re-execution of the underlying query)\n",[60,904,905,907,909,911,914,916,919,922,925],{"class":62,"line":310},[60,906,26],{"class":73},[60,908,241],{"class":73},[60,910,244],{"class":73},[60,912,913],{"class":100}," daily_sales_summary ",[60,915,250],{"class":73},[60,917,918],{"class":100}," sale_date ",[60,920,921],{"class":73},">=",[60,923,924],{"class":258}," '2026-06-01'",[60,926,220],{"class":100},[60,928,929],{"class":62,"line":318},[60,930,227],{"emptyLinePlaceholder":226},[60,932,934],{"class":62,"line":933},18,[60,935,936],{"class":66},"-- Refresh when source data changes\n",[60,938,940],{"class":62,"line":939},19,[60,941,942],{"class":100},"REFRESH MATERIALIZED VIEW daily_sales_summary;\n",[60,944,946],{"class":62,"line":945},20,[60,947,227],{"emptyLinePlaceholder":226},[60,949,951],{"class":62,"line":950},21,[60,952,953],{"class":66},"-- Refresh without locking reads (Postgres, requires a UNIQUE index)\n",[60,955,957,959,962,964],{"class":62,"line":956},22,[60,958,357],{"class":73},[60,960,961],{"class":73}," UNIQUE INDEX",[60,963,408],{"class":80},[60,965,966],{"class":100}," daily_sales_summary (sale_date, category);\n",[60,968,970],{"class":62,"line":969},23,[60,971,972],{"class":100},"REFRESH MATERIALIZED VIEW CONCURRENTLY daily_sales_summary;\n",[10,974,976],{"id":975},"view-vs-cte-vs-temp-table-vs-materialized-view","View vs CTE vs temp table vs materialized view",[978,979,980,1001],"table",{},[981,982,983],"thead",{},[984,985,986,989,992,995,998],"tr",{},[987,988],"th",{},[987,990,991],{},"View",[987,993,994],{},"CTE",[987,996,997],{},"Temp table",[987,999,1000],{},"Materialized view",[1002,1003,1004,1021,1034,1047,1064],"tbody",{},[984,1005,1006,1010,1013,1016,1019],{},[1007,1008,1009],"td",{},"Persists",[1007,1011,1012],{},"✅",[1007,1014,1015],{},"❌",[1007,1017,1018],{},"Session only",[1007,1020,1012],{},[984,1022,1023,1026,1028,1030,1032],{},[1007,1024,1025],{},"Stores data",[1007,1027,1015],{},[1007,1029,1015],{},[1007,1031,1012],{},[1007,1033,1012],{},[984,1035,1036,1039,1041,1043,1045],{},[1007,1037,1038],{},"Indexable",[1007,1040,1015],{},[1007,1042,1015],{},[1007,1044,1012],{},[1007,1046,1012],{},[984,1048,1049,1052,1055,1058,1061],{},[1007,1050,1051],{},"Auto-updated",[1007,1053,1054],{},"✅ (live)",[1007,1056,1057],{},"N\u002FA",[1007,1059,1060],{},"Manual",[1007,1062,1063],{},"Manual refresh",[984,1065,1066,1069,1071,1073,1075],{},[1007,1067,1068],{},"Access control",[1007,1070,1012],{},[1007,1072,1015],{},[1007,1074,1015],{},[1007,1076,1012],{},[10,1078,1080],{"id":1079},"recursive-views-postgres","Recursive views (Postgres)",[51,1082,1084],{"className":53,"code":1083,"language":55,"meta":56,"style":56},"CREATE RECURSIVE VIEW category_tree (id, name, parent_id, depth) AS\n    SELECT id, name, parent_id, 0\n    FROM   categories WHERE parent_id IS NULL\n    UNION ALL\n    SELECT c.id, c.name, c.parent_id, ct.depth + 1\n    FROM   categories c JOIN category_tree ct ON c.parent_id = ct.id;\n",[24,1085,1086,1104,1120,1138,1143,1187],{"__ignoreMap":56},[60,1087,1088,1090,1093,1096,1099,1102],{"class":62,"line":63},[60,1089,357],{"class":73},[60,1091,1092],{"class":73}," RECURSIVE",[60,1094,1095],{"class":100}," VIEW category_tree (id, ",[60,1097,1098],{"class":73},"name",[60,1100,1101],{"class":100},", parent_id, depth) ",[60,1103,689],{"class":73},[60,1105,1106,1109,1112,1114,1117],{"class":62,"line":70},[60,1107,1108],{"class":73},"    SELECT",[60,1110,1111],{"class":100}," id, ",[60,1113,1098],{"class":73},[60,1115,1116],{"class":100},", parent_id, ",[60,1118,1119],{"class":96},"0\n",[60,1121,1122,1125,1128,1130,1133,1135],{"class":62,"line":87},[60,1123,1124],{"class":73},"    FROM",[60,1126,1127],{"class":100},"   categories ",[60,1129,250],{"class":73},[60,1131,1132],{"class":100}," parent_id ",[60,1134,388],{"class":73},[60,1136,1137],{"class":73}," NULL\n",[60,1139,1140],{"class":62,"line":93},[60,1141,1142],{"class":73},"    UNION ALL\n",[60,1144,1145,1147,1149,1151,1153,1155,1158,1160,1162,1164,1166,1168,1171,1173,1176,1178,1181,1184],{"class":62,"line":113},[60,1146,1108],{"class":73},[60,1148,202],{"class":96},[60,1150,101],{"class":100},[60,1152,104],{"class":96},[60,1154,502],{"class":100},[60,1156,1157],{"class":96},"c",[60,1159,101],{"class":100},[60,1161,1098],{"class":96},[60,1163,502],{"class":100},[60,1165,1157],{"class":96},[60,1167,101],{"class":100},[60,1169,1170],{"class":96},"parent_id",[60,1172,502],{"class":100},[60,1174,1175],{"class":96},"ct",[60,1177,101],{"class":100},[60,1179,1180],{"class":96},"depth",[60,1182,1183],{"class":73}," +",[60,1185,1186],{"class":96}," 1\n",[60,1188,1189,1191,1194,1196,1199,1201,1203,1205,1207,1209,1212,1214,1216],{"class":62,"line":130},[60,1190,1124],{"class":73},[60,1192,1193],{"class":100},"   categories c ",[60,1195,193],{"class":73},[60,1197,1198],{"class":100}," category_tree ct ",[60,1200,199],{"class":73},[60,1202,202],{"class":96},[60,1204,101],{"class":100},[60,1206,1170],{"class":96},[60,1208,209],{"class":73},[60,1210,1211],{"class":96}," ct",[60,1213,101],{"class":100},[60,1215,104],{"class":96},[60,1217,220],{"class":100},[10,1219,1221],{"id":1220},"recap","Recap",[15,1223,1224,1225,1228,1229,1232,1233,1235,1236,1238],{},"Regular views simplify complex queries and enforce access control — they are\nalways up-to-date but re-execute every time. Materialized views sacrifice\ncurrency for performance — schedule ",[24,1226,1227],{},"REFRESH"," to match your staleness tolerance.\nUse ",[24,1230,1231],{},"CONCURRENTLY"," refresh on Postgres (requires a unique index) to avoid\nblocking readers during the refresh. Keep views updatable by avoiding aggregates\nand ",[24,1234,501],{},"; use ",[24,1237,658],{}," triggers for complex update paths.",[1240,1241,1242],"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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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);}",{"title":56,"searchDepth":70,"depth":70,"links":1244},[1245,1246,1247,1248,1249,1250,1251,1252],{"id":12,"depth":70,"text":13},{"id":48,"depth":70,"text":49},{"id":333,"depth":70,"text":334},{"id":490,"depth":70,"text":491},{"id":662,"depth":70,"text":663},{"id":975,"depth":70,"text":976},{"id":1079,"depth":70,"text":1080},{"id":1220,"depth":70,"text":1221},"SQL views explained — regular vs materialized views, updatable views, security views, view performance, and when to use each type with real examples.","medium","md","SQL",{},"\u002Fblog\u002Fsql-views-virtual-tables","\u002Fsql\u002Fdml\u002Fviews",{"title":5,"description":1253},"blog\u002Fsql-views-virtual-tables","Views","Modifying Data","dml","2026-06-20","-blPn-p6dMzC6TwGO9jjIyYDS5eL3QpVOwwXuqdltfU",1782244088656]