[{"data":1,"prerenderedAt":1054},["ShallowReactive",2],{"blog-\u002Fblog\u002Fsql-indexes-btree-performance":3},{"id":4,"title":5,"body":6,"description":1040,"difficulty":1041,"extension":1042,"framework":1043,"frameworkSlug":46,"meta":1044,"navigation":86,"order":55,"path":1045,"qaPath":1046,"seo":1047,"stem":1048,"subtopic":1049,"topic":1050,"topicSlug":1051,"updated":1052,"__hash__":1053},"blog\u002Fblog\u002Fsql-indexes-btree-performance.md","SQL Indexes — B-tree, Composite, Partial, and Covering Indexes",{"type":7,"value":8,"toc":1030},"minimark",[9,14,27,30,34,41,181,197,250,254,261,280,290,451,466,470,476,594,598,609,690,700,704,891,895,1007,1011,1026],[10,11,13],"h2",{"id":12},"what-an-index-is","What an index is",[15,16,17,18,22,23,26],"p",{},"An index is a separate data structure that maps column values to the physical\nlocation of matching rows. Without an index, the database reads every row to\nfind matches — a ",[19,20,21],"strong",{},"sequential scan",". With an index it jumps directly to the\nrelevant rows — an ",[19,24,25],{},"index scan"," — which is orders of magnitude faster on large\ntables.",[15,28,29],{},"The tradeoff: indexes consume disk space and slow down writes (every INSERT,\nUPDATE, and DELETE must also update every index on the table).",[10,31,33],{"id":32},"b-tree-indexes-the-default","B-tree indexes — the default",[15,35,36,37,40],{},"The default index type in every major database is a ",[19,38,39],{},"B-tree"," (balanced tree).\nIt keeps values in sorted order, enabling fast equality lookups, range scans,\nand sorts.",[42,43,48],"pre",{"className":44,"code":45,"language":46,"meta":47,"style":47},"language-sql shiki shiki-themes github-light github-dark","-- Creating a B-tree index (explicitly, and the default shorthand)\nCREATE INDEX idx_orders_customer_id ON orders (customer_id);\n\n-- The database can now use this index for:\nSELECT * FROM orders WHERE customer_id = 1001;           -- equality\nSELECT * FROM orders WHERE customer_id BETWEEN 100 AND 200; -- range\nSELECT * FROM orders ORDER BY customer_id;               -- sort (avoids sort step)\n","sql","",[49,50,51,60,81,88,94,128,161],"code",{"__ignoreMap":47},[52,53,56],"span",{"class":54,"line":55},"line",1,[52,57,59],{"class":58},"sJ8bj","-- Creating a B-tree index (explicitly, and the default shorthand)\n",[52,61,63,67,70,74,77],{"class":54,"line":62},2,[52,64,66],{"class":65},"szBVR","CREATE",[52,68,69],{"class":65}," INDEX",[52,71,73],{"class":72},"sScJk"," idx_orders_customer_id",[52,75,76],{"class":65}," ON",[52,78,80],{"class":79},"sVt8B"," orders (customer_id);\n",[52,82,84],{"class":54,"line":83},3,[52,85,87],{"emptyLinePlaceholder":86},true,"\n",[52,89,91],{"class":54,"line":90},4,[52,92,93],{"class":58},"-- The database can now use this index for:\n",[52,95,97,100,103,106,109,112,115,118,122,125],{"class":54,"line":96},5,[52,98,99],{"class":65},"SELECT",[52,101,102],{"class":65}," *",[52,104,105],{"class":65}," FROM",[52,107,108],{"class":79}," orders ",[52,110,111],{"class":65},"WHERE",[52,113,114],{"class":79}," customer_id ",[52,116,117],{"class":65},"=",[52,119,121],{"class":120},"sj4cs"," 1001",[52,123,124],{"class":79},";           ",[52,126,127],{"class":58},"-- equality\n",[52,129,131,133,135,137,139,141,143,146,149,152,155,158],{"class":54,"line":130},6,[52,132,99],{"class":65},[52,134,102],{"class":65},[52,136,105],{"class":65},[52,138,108],{"class":79},[52,140,111],{"class":65},[52,142,114],{"class":79},[52,144,145],{"class":65},"BETWEEN",[52,147,148],{"class":120}," 100",[52,150,151],{"class":65}," AND",[52,153,154],{"class":120}," 200",[52,156,157],{"class":79},"; ",[52,159,160],{"class":58},"-- range\n",[52,162,164,166,168,170,172,175,178],{"class":54,"line":163},7,[52,165,99],{"class":65},[52,167,102],{"class":65},[52,169,105],{"class":65},[52,171,108],{"class":79},[52,173,174],{"class":65},"ORDER BY",[52,176,177],{"class":79}," customer_id;               ",[52,179,180],{"class":58},"-- sort (avoids sort step)\n",[15,182,183,184,187,188,192,193,196],{},"Primary keys and ",[49,185,186],{},"UNIQUE"," constraints create B-tree indexes automatically.\nForeign key columns on the ",[189,190,191],"em",{},"referencing"," side do ",[19,194,195],{},"not"," get automatic indexes\n— create them manually.",[42,198,200],{"className":44,"code":199,"language":46,"meta":47,"style":47},"-- Foreign key columns that need manual indexes\nCREATE INDEX idx_orders_customer_id      ON orders      (customer_id);\nCREATE INDEX idx_order_items_order_id   ON order_items (order_id);\nCREATE INDEX idx_order_items_product_id ON order_items (product_id);\n",[49,201,202,207,221,236],{"__ignoreMap":47},[52,203,204],{"class":54,"line":55},[52,205,206],{"class":58},"-- Foreign key columns that need manual indexes\n",[52,208,209,211,213,215,218],{"class":54,"line":62},[52,210,66],{"class":65},[52,212,69],{"class":65},[52,214,73],{"class":72},[52,216,217],{"class":65},"      ON",[52,219,220],{"class":79}," orders      (customer_id);\n",[52,222,223,225,227,230,233],{"class":54,"line":83},[52,224,66],{"class":65},[52,226,69],{"class":65},[52,228,229],{"class":72}," idx_order_items_order_id",[52,231,232],{"class":65},"   ON",[52,234,235],{"class":79}," order_items (order_id);\n",[52,237,238,240,242,245,247],{"class":54,"line":90},[52,239,66],{"class":65},[52,241,69],{"class":65},[52,243,244],{"class":72}," idx_order_items_product_id",[52,246,76],{"class":65},[52,248,249],{"class":79}," order_items (product_id);\n",[10,251,253],{"id":252},"composite-indexes-column-order-matters","Composite indexes — column order matters",[15,255,256,257,260],{},"A composite index on ",[49,258,259],{},"(col_a, col_b)"," can be used for queries on:",[262,263,264,271],"ul",{},[265,266,267,270],"li",{},[49,268,269],{},"col_a"," alone",[265,272,273,275,276,279],{},[49,274,269],{}," AND ",[49,277,278],{},"col_b"," together",[15,281,282,283,285,286,289],{},"It cannot be used for ",[49,284,278],{}," alone (the ",[19,287,288],{},"leftmost prefix rule",").",[42,291,293],{"className":44,"code":292,"language":46,"meta":47,"style":47},"-- Good composite index: supports both filtering by customer and sorting by date\nCREATE INDEX idx_orders_customer_created\n    ON orders (customer_id, created_at DESC);\n\n-- Supported queries:\nSELECT * FROM orders WHERE customer_id = 1001;\nSELECT * FROM orders WHERE customer_id = 1001 ORDER BY created_at DESC;\nSELECT * FROM orders WHERE customer_id = 1001 AND created_at > '2026-01-01';\n\n-- Not supported (no customer_id prefix):\nSELECT * FROM orders WHERE created_at > '2026-01-01';\n-- ^ requires a separate index on (created_at)\n",[49,294,295,300,309,323,327,332,353,381,413,418,424,445],{"__ignoreMap":47},[52,296,297],{"class":54,"line":55},[52,298,299],{"class":58},"-- Good composite index: supports both filtering by customer and sorting by date\n",[52,301,302,304,306],{"class":54,"line":62},[52,303,66],{"class":65},[52,305,69],{"class":65},[52,307,308],{"class":72}," idx_orders_customer_created\n",[52,310,311,314,317,320],{"class":54,"line":83},[52,312,313],{"class":65},"    ON",[52,315,316],{"class":79}," orders (customer_id, created_at ",[52,318,319],{"class":65},"DESC",[52,321,322],{"class":79},");\n",[52,324,325],{"class":54,"line":90},[52,326,87],{"emptyLinePlaceholder":86},[52,328,329],{"class":54,"line":96},[52,330,331],{"class":58},"-- Supported queries:\n",[52,333,334,336,338,340,342,344,346,348,350],{"class":54,"line":130},[52,335,99],{"class":65},[52,337,102],{"class":65},[52,339,105],{"class":65},[52,341,108],{"class":79},[52,343,111],{"class":65},[52,345,114],{"class":79},[52,347,117],{"class":65},[52,349,121],{"class":120},[52,351,352],{"class":79},";\n",[52,354,355,357,359,361,363,365,367,369,371,374,377,379],{"class":54,"line":163},[52,356,99],{"class":65},[52,358,102],{"class":65},[52,360,105],{"class":65},[52,362,108],{"class":79},[52,364,111],{"class":65},[52,366,114],{"class":79},[52,368,117],{"class":65},[52,370,121],{"class":120},[52,372,373],{"class":65}," ORDER BY",[52,375,376],{"class":79}," created_at ",[52,378,319],{"class":65},[52,380,352],{"class":79},[52,382,384,386,388,390,392,394,396,398,400,402,404,407,411],{"class":54,"line":383},8,[52,385,99],{"class":65},[52,387,102],{"class":65},[52,389,105],{"class":65},[52,391,108],{"class":79},[52,393,111],{"class":65},[52,395,114],{"class":79},[52,397,117],{"class":65},[52,399,121],{"class":120},[52,401,151],{"class":65},[52,403,376],{"class":79},[52,405,406],{"class":65},">",[52,408,410],{"class":409},"sZZnC"," '2026-01-01'",[52,412,352],{"class":79},[52,414,416],{"class":54,"line":415},9,[52,417,87],{"emptyLinePlaceholder":86},[52,419,421],{"class":54,"line":420},10,[52,422,423],{"class":58},"-- Not supported (no customer_id prefix):\n",[52,425,427,429,431,433,435,437,439,441,443],{"class":54,"line":426},11,[52,428,99],{"class":65},[52,430,102],{"class":65},[52,432,105],{"class":65},[52,434,108],{"class":79},[52,436,111],{"class":65},[52,438,376],{"class":79},[52,440,406],{"class":65},[52,442,410],{"class":409},[52,444,352],{"class":79},[52,446,448],{"class":54,"line":447},12,[52,449,450],{"class":58},"-- ^ requires a separate index on (created_at)\n",[15,452,453,454,457,458,461,462,465],{},"Put the ",[19,455,456],{},"equality columns first",", ",[19,459,460],{},"range columns last",", and the ",[19,463,464],{},"ORDER BY\ncolumn last"," (if it matches the range direction).",[10,467,469],{"id":468},"partial-indexes-index-a-subset-of-rows","Partial indexes — index a subset of rows",[15,471,472,473,475],{},"A partial index only indexes rows that satisfy a ",[49,474,111],{}," condition. It is\nsmaller, faster to maintain, and more selective than a full-column index.",[42,477,479],{"className":44,"code":478,"language":46,"meta":47,"style":47},"-- Index only pending orders (the ones that need the most lookups)\nCREATE INDEX idx_orders_pending\n    ON orders (customer_id, created_at)\n    WHERE status = 'pending';\n\n-- Queries that can use this index must include the same condition\nSELECT * FROM orders WHERE customer_id = 1001 AND status = 'pending';\n\n-- Partial unique index: only one active subscription per customer\nCREATE UNIQUE INDEX idx_subscriptions_active_customer\n    ON subscriptions (customer_id)\n    WHERE status = 'active';\n",[49,480,481,486,495,502,518,522,527,555,559,564,574,581],{"__ignoreMap":47},[52,482,483],{"class":54,"line":55},[52,484,485],{"class":58},"-- Index only pending orders (the ones that need the most lookups)\n",[52,487,488,490,492],{"class":54,"line":62},[52,489,66],{"class":65},[52,491,69],{"class":65},[52,493,494],{"class":72}," idx_orders_pending\n",[52,496,497,499],{"class":54,"line":83},[52,498,313],{"class":65},[52,500,501],{"class":79}," orders (customer_id, created_at)\n",[52,503,504,507,510,513,516],{"class":54,"line":90},[52,505,506],{"class":65},"    WHERE",[52,508,509],{"class":65}," status",[52,511,512],{"class":65}," =",[52,514,515],{"class":409}," 'pending'",[52,517,352],{"class":79},[52,519,520],{"class":54,"line":96},[52,521,87],{"emptyLinePlaceholder":86},[52,523,524],{"class":54,"line":130},[52,525,526],{"class":58},"-- Queries that can use this index must include the same condition\n",[52,528,529,531,533,535,537,539,541,543,545,547,549,551,553],{"class":54,"line":163},[52,530,99],{"class":65},[52,532,102],{"class":65},[52,534,105],{"class":65},[52,536,108],{"class":79},[52,538,111],{"class":65},[52,540,114],{"class":79},[52,542,117],{"class":65},[52,544,121],{"class":120},[52,546,151],{"class":65},[52,548,509],{"class":65},[52,550,512],{"class":65},[52,552,515],{"class":409},[52,554,352],{"class":79},[52,556,557],{"class":54,"line":383},[52,558,87],{"emptyLinePlaceholder":86},[52,560,561],{"class":54,"line":415},[52,562,563],{"class":58},"-- Partial unique index: only one active subscription per customer\n",[52,565,566,568,571],{"class":54,"line":420},[52,567,66],{"class":65},[52,569,570],{"class":65}," UNIQUE INDEX",[52,572,573],{"class":72}," idx_subscriptions_active_customer\n",[52,575,576,578],{"class":54,"line":426},[52,577,313],{"class":65},[52,579,580],{"class":79}," subscriptions (customer_id)\n",[52,582,583,585,587,589,592],{"class":54,"line":447},[52,584,506],{"class":65},[52,586,509],{"class":65},[52,588,512],{"class":65},[52,590,591],{"class":409}," 'active'",[52,593,352],{"class":79},[10,595,597],{"id":596},"covering-indexes-eliminate-heap-fetches","Covering indexes — eliminate heap fetches",[15,599,600,601,604,605,608],{},"A ",[19,602,603],{},"covering index"," includes all columns a query needs, so the database never\nhas to read the main table (heap) at all — an ",[19,606,607],{},"Index Only Scan",".",[42,610,612],{"className":44,"code":611,"language":46,"meta":47,"style":47},"-- Query: SELECT email FROM users WHERE created_at > '2026-01-01'\n-- A covering index using INCLUDE (Postgres 11+):\nCREATE INDEX idx_users_created_covering\n    ON users (created_at DESC)\n    INCLUDE (email);\n\n-- EXPLAIN shows: Index Only Scan (Heap Fetches: 0)\nEXPLAIN (ANALYZE, BUFFERS)\nSELECT email FROM users WHERE created_at > '2026-01-01';\n",[49,613,614,619,624,633,645,653,657,662,667],{"__ignoreMap":47},[52,615,616],{"class":54,"line":55},[52,617,618],{"class":58},"-- Query: SELECT email FROM users WHERE created_at > '2026-01-01'\n",[52,620,621],{"class":54,"line":62},[52,622,623],{"class":58},"-- A covering index using INCLUDE (Postgres 11+):\n",[52,625,626,628,630],{"class":54,"line":83},[52,627,66],{"class":65},[52,629,69],{"class":65},[52,631,632],{"class":72}," idx_users_created_covering\n",[52,634,635,637,640,642],{"class":54,"line":90},[52,636,313],{"class":65},[52,638,639],{"class":79}," users (created_at ",[52,641,319],{"class":65},[52,643,644],{"class":79},")\n",[52,646,647,650],{"class":54,"line":96},[52,648,649],{"class":65},"    INCLUDE",[52,651,652],{"class":79}," (email);\n",[52,654,655],{"class":54,"line":130},[52,656,87],{"emptyLinePlaceholder":86},[52,658,659],{"class":54,"line":163},[52,660,661],{"class":58},"-- EXPLAIN shows: Index Only Scan (Heap Fetches: 0)\n",[52,663,664],{"class":54,"line":383},[52,665,666],{"class":79},"EXPLAIN (ANALYZE, BUFFERS)\n",[52,668,669,671,674,677,680,682,684,686,688],{"class":54,"line":415},[52,670,99],{"class":65},[52,672,673],{"class":79}," email ",[52,675,676],{"class":65},"FROM",[52,678,679],{"class":79}," users ",[52,681,111],{"class":65},[52,683,376],{"class":79},[52,685,406],{"class":65},[52,687,410],{"class":409},[52,689,352],{"class":79},[15,691,692,693,696,697,608],{},"The ",[49,694,695],{},"INCLUDE"," columns are stored in the leaf nodes of the index but are not\npart of the sort order. The index is still B-tree sorted on ",[49,698,699],{},"created_at",[10,701,703],{"id":702},"what-prevents-index-use","What prevents index use",[42,705,707],{"className":44,"code":706,"language":46,"meta":47,"style":47},"-- 1. Function wrapped around the indexed column — index on email is unused\nSELECT * FROM users WHERE LOWER(email) = 'alice@example.com';\n-- Fix: create a functional index\nCREATE INDEX idx_users_email_lower ON users (LOWER(email));\n\n-- 2. Leading wildcard in LIKE — cannot scan B-tree from the front\nSELECT * FROM products WHERE product_name LIKE '%keyboard%';\n-- Fix: use full-text search or a TRIGRAM index (pg_trgm extension in Postgres)\n\n-- 3. Type mismatch — implicit cast prevents index use\nSELECT * FROM orders WHERE customer_id = '1001';  -- '1001' is text, column is INT\n-- Fix: match the literal type: customer_id = 1001\n\n-- 4. OR conditions that span different indexed columns — usually a full scan\nSELECT * FROM users WHERE email = 'a@b.com' OR phone = '555-1234';\n-- Fix: UNION instead of OR, or a multi-column index that covers both\n",[49,708,709,714,739,744,764,768,773,797,802,806,811,836,841,846,852,885],{"__ignoreMap":47},[52,710,711],{"class":54,"line":55},[52,712,713],{"class":58},"-- 1. Function wrapped around the indexed column — index on email is unused\n",[52,715,716,718,720,722,724,726,729,732,734,737],{"class":54,"line":62},[52,717,99],{"class":65},[52,719,102],{"class":65},[52,721,105],{"class":65},[52,723,679],{"class":79},[52,725,111],{"class":65},[52,727,728],{"class":120}," LOWER",[52,730,731],{"class":79},"(email) ",[52,733,117],{"class":65},[52,735,736],{"class":409}," 'alice@example.com'",[52,738,352],{"class":79},[52,740,741],{"class":54,"line":83},[52,742,743],{"class":58},"-- Fix: create a functional index\n",[52,745,746,748,750,753,755,758,761],{"class":54,"line":90},[52,747,66],{"class":65},[52,749,69],{"class":65},[52,751,752],{"class":72}," idx_users_email_lower",[52,754,76],{"class":65},[52,756,757],{"class":79}," users (",[52,759,760],{"class":120},"LOWER",[52,762,763],{"class":79},"(email));\n",[52,765,766],{"class":54,"line":96},[52,767,87],{"emptyLinePlaceholder":86},[52,769,770],{"class":54,"line":130},[52,771,772],{"class":58},"-- 2. Leading wildcard in LIKE — cannot scan B-tree from the front\n",[52,774,775,777,779,781,784,786,789,792,795],{"class":54,"line":163},[52,776,99],{"class":65},[52,778,102],{"class":65},[52,780,105],{"class":65},[52,782,783],{"class":79}," products ",[52,785,111],{"class":65},[52,787,788],{"class":79}," product_name ",[52,790,791],{"class":65},"LIKE",[52,793,794],{"class":409}," '%keyboard%'",[52,796,352],{"class":79},[52,798,799],{"class":54,"line":383},[52,800,801],{"class":58},"-- Fix: use full-text search or a TRIGRAM index (pg_trgm extension in Postgres)\n",[52,803,804],{"class":54,"line":415},[52,805,87],{"emptyLinePlaceholder":86},[52,807,808],{"class":54,"line":420},[52,809,810],{"class":58},"-- 3. Type mismatch — implicit cast prevents index use\n",[52,812,813,815,817,819,821,823,825,827,830,833],{"class":54,"line":426},[52,814,99],{"class":65},[52,816,102],{"class":65},[52,818,105],{"class":65},[52,820,108],{"class":79},[52,822,111],{"class":65},[52,824,114],{"class":79},[52,826,117],{"class":65},[52,828,829],{"class":409}," '1001'",[52,831,832],{"class":79},";  ",[52,834,835],{"class":58},"-- '1001' is text, column is INT\n",[52,837,838],{"class":54,"line":447},[52,839,840],{"class":58},"-- Fix: match the literal type: customer_id = 1001\n",[52,842,844],{"class":54,"line":843},13,[52,845,87],{"emptyLinePlaceholder":86},[52,847,849],{"class":54,"line":848},14,[52,850,851],{"class":58},"-- 4. OR conditions that span different indexed columns — usually a full scan\n",[52,853,855,857,859,861,863,865,867,869,872,875,878,880,883],{"class":54,"line":854},15,[52,856,99],{"class":65},[52,858,102],{"class":65},[52,860,105],{"class":65},[52,862,679],{"class":79},[52,864,111],{"class":65},[52,866,673],{"class":79},[52,868,117],{"class":65},[52,870,871],{"class":409}," 'a@b.com'",[52,873,874],{"class":65}," OR",[52,876,877],{"class":79}," phone ",[52,879,117],{"class":65},[52,881,882],{"class":409}," '555-1234'",[52,884,352],{"class":79},[52,886,888],{"class":54,"line":887},16,[52,889,890],{"class":58},"-- Fix: UNION instead of OR, or a multi-column index that covers both\n",[10,892,894],{"id":893},"identifying-missing-indexes","Identifying missing indexes",[42,896,898],{"className":44,"code":897,"language":46,"meta":47,"style":47},"-- Postgres: find sequential scans on large tables (candidates for indexing)\nSELECT relname AS table, seq_scan, seq_tup_read, idx_scan\nFROM   pg_stat_user_tables\nWHERE  seq_scan > 0\nORDER  BY seq_tup_read DESC\nLIMIT  20;\n\n-- Check if a specific query uses an index\nEXPLAIN (ANALYZE, BUFFERS)\nSELECT * FROM orders WHERE customer_id = 1001 AND status = 'pending';\n-- Look for: Index Scan or Index Only Scan (good) vs Seq Scan (investigate)\n",[49,899,900,905,921,928,940,951,961,965,970,974,1002],{"__ignoreMap":47},[52,901,902],{"class":54,"line":55},[52,903,904],{"class":58},"-- Postgres: find sequential scans on large tables (candidates for indexing)\n",[52,906,907,909,912,915,918],{"class":54,"line":62},[52,908,99],{"class":65},[52,910,911],{"class":79}," relname ",[52,913,914],{"class":65},"AS",[52,916,917],{"class":65}," table",[52,919,920],{"class":79},", seq_scan, seq_tup_read, idx_scan\n",[52,922,923,925],{"class":54,"line":83},[52,924,676],{"class":65},[52,926,927],{"class":79},"   pg_stat_user_tables\n",[52,929,930,932,935,937],{"class":54,"line":90},[52,931,111],{"class":65},[52,933,934],{"class":79},"  seq_scan ",[52,936,406],{"class":65},[52,938,939],{"class":120}," 0\n",[52,941,942,945,948],{"class":54,"line":96},[52,943,944],{"class":65},"ORDER  BY",[52,946,947],{"class":79}," seq_tup_read ",[52,949,950],{"class":65},"DESC\n",[52,952,953,956,959],{"class":54,"line":130},[52,954,955],{"class":65},"LIMIT",[52,957,958],{"class":120},"  20",[52,960,352],{"class":79},[52,962,963],{"class":54,"line":163},[52,964,87],{"emptyLinePlaceholder":86},[52,966,967],{"class":54,"line":383},[52,968,969],{"class":58},"-- Check if a specific query uses an index\n",[52,971,972],{"class":54,"line":415},[52,973,666],{"class":79},[52,975,976,978,980,982,984,986,988,990,992,994,996,998,1000],{"class":54,"line":420},[52,977,99],{"class":65},[52,979,102],{"class":65},[52,981,105],{"class":65},[52,983,108],{"class":79},[52,985,111],{"class":65},[52,987,114],{"class":79},[52,989,117],{"class":65},[52,991,121],{"class":120},[52,993,151],{"class":65},[52,995,509],{"class":65},[52,997,512],{"class":65},[52,999,515],{"class":409},[52,1001,352],{"class":79},[52,1003,1004],{"class":54,"line":426},[52,1005,1006],{"class":58},"-- Look for: Index Scan or Index Only Scan (good) vs Seq Scan (investigate)\n",[10,1008,1010],{"id":1009},"recap","Recap",[15,1012,1013,1014,457,1016,1019,1020,1022,1023,1025],{},"Create B-tree indexes on every foreign key column (the referencing side) and on\nevery column that appears in ",[49,1015,111],{},[49,1017,1018],{},"JOIN ON",", or ",[49,1021,174],{}," for high-traffic\nqueries. For composite indexes, equality columns go first, range columns last.\nUse partial indexes to index only the active\u002Fpending subset of hot-path queries.\nUse ",[49,1024,695],{}," to build covering indexes that eliminate heap fetches. Avoid\nwrapping indexed columns in functions — create functional indexes instead.",[1027,1028,1029],"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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":47,"searchDepth":62,"depth":62,"links":1031},[1032,1033,1034,1035,1036,1037,1038,1039],{"id":12,"depth":62,"text":13},{"id":32,"depth":62,"text":33},{"id":252,"depth":62,"text":253},{"id":468,"depth":62,"text":469},{"id":596,"depth":62,"text":597},{"id":702,"depth":62,"text":703},{"id":893,"depth":62,"text":894},{"id":1009,"depth":62,"text":1010},"SQL indexes explained — B-tree internals, composite index column order, partial indexes, covering indexes with INCLUDE, and what kills index use.","medium","md","SQL",{},"\u002Fblog\u002Fsql-indexes-btree-performance","\u002Fsql\u002Fperformance\u002Findexes",{"title":5,"description":1040},"blog\u002Fsql-indexes-btree-performance","Indexes","Indexes & Performance","performance","2026-06-20","tduQSlIj27NfqCIqfQT-OAhVIZWc6UGDvSYqEqp5xa8",1782244088168]