[{"data":1,"prerenderedAt":3323},["ShallowReactive",2],{"page-\u002Fsql\u002F02-select-basics-and-filtering":3},{"id":4,"title":5,"body":6,"description":35,"extension":3317,"meta":3318,"navigation":158,"path":3319,"seo":3320,"stem":3321,"__hash__":3322},"content\u002Fsql\u002F02-select-basics-and-filtering.md","02 — SELECT Basics & Filtering",{"type":7,"value":8,"toc":3289},"minimark",[9,13,18,26,87,118,122,291,299,348,353,386,389,393,530,546,550,596,609,616,674,677,681,698,702,911,1154,1158,1200,1216,1365,1369,1449,1492,1509,1595,1599,1771,1780,1784,1800,1878,1963,1974,1978,2059,2068,2072,2081,2246,2250,2499,2506,2581,2585,2604,2692,2772,2776,2865,2982,3074,3078,3087,3114,3276,3279,3285],[10,11,5],"h1",{"id":12},"_02-select-basics-filtering",[14,15,17],"h2",{"id":16},"the-select-statement","The SELECT Statement",[19,20,21,25],"p",{},[22,23,24],"code",{},"SELECT"," is the workhorse of SQL. Its core form:",[27,28,30],"code-wrapper",{"language":29},"sql",[31,32,36],"pre",{"className":33,"code":34,"language":29,"meta":35,"style":35},"language-sql shiki shiki-themes github-light github-dark","-- projection: which columns to return\n-- selection:  which rows to return (WHERE)\n-- source:     which table (or join\u002FCTE\u002Fsubquery) to read from\nSELECT column1, column2\nFROM table_name\nWHERE condition;\n","",[22,37,38,47,53,59,69,78],{"__ignoreMap":35},[39,40,43],"span",{"class":41,"line":42},"line",1,[39,44,46],{"class":45},"sdCPZ","-- projection: which columns to return\n",[39,48,50],{"class":41,"line":49},2,[39,51,52],{"class":45},"-- selection:  which rows to return (WHERE)\n",[39,54,56],{"class":41,"line":55},3,[39,57,58],{"class":45},"-- source:     which table (or join\u002FCTE\u002Fsubquery) to read from\n",[39,60,62,65],{"class":41,"line":61},4,[39,63,24],{"class":64},"svdQ7",[39,66,68],{"class":67},"ssxIu"," column1, column2\n",[39,70,72,75],{"class":41,"line":71},5,[39,73,74],{"class":64},"FROM",[39,76,77],{"class":67}," table_name\n",[39,79,81,84],{"class":41,"line":80},6,[39,82,83],{"class":64},"WHERE",[39,85,86],{"class":67}," condition;\n",[88,89,90,101,110],"ul",{},[91,92,93,95,96,100],"li",{},[22,94,24],{}," chooses ",[97,98,99],"strong",{},"columns"," (projection).",[91,102,103,105,106,109],{},[22,104,74],{}," chooses the ",[97,107,108],{},"source"," (a table, or later: a join, subquery, CTE).",[91,111,112,95,114,117],{},[22,113,83],{},[97,115,116],{},"rows"," (selection \u002F filtering).",[14,119,121],{"id":120},"selecting-columns","Selecting Columns",[27,123,124],{"language":29},[31,125,127],{"className":33,"code":126,"language":29,"meta":35,"style":35},"-- Explicit columns — the only safe form for application code.\n-- The planner can use covering indexes that include exactly these columns.\nSELECT name, city FROM customers;\n\n-- Expressions with aliases\nSELECT\n  name,\n  length(name) AS name_len,               -- function call + alias\n  upper(city) AS city_upper\nFROM customers;\n\n-- Constants and computations\nSELECT\n  name,\n  'US' AS country,                        -- literal column\n  2024 - 2000 AS age_estimate             -- arithmetic on literals\nFROM customers;\n",[22,128,129,134,139,154,160,165,170,179,203,218,225,230,236,241,248,264,284],{"__ignoreMap":35},[39,130,131],{"class":41,"line":42},[39,132,133],{"class":45},"-- Explicit columns — the only safe form for application code.\n",[39,135,136],{"class":41,"line":49},[39,137,138],{"class":45},"-- The planner can use covering indexes that include exactly these columns.\n",[39,140,141,143,146,149,151],{"class":41,"line":55},[39,142,24],{"class":64},[39,144,145],{"class":64}," name",[39,147,148],{"class":67},", city ",[39,150,74],{"class":64},[39,152,153],{"class":67}," customers;\n",[39,155,156],{"class":41,"line":61},[39,157,159],{"emptyLinePlaceholder":158},true,"\n",[39,161,162],{"class":41,"line":71},[39,163,164],{"class":45},"-- Expressions with aliases\n",[39,166,167],{"class":41,"line":80},[39,168,169],{"class":64},"SELECT\n",[39,171,173,176],{"class":41,"line":172},7,[39,174,175],{"class":64},"  name",[39,177,178],{"class":67},",\n",[39,180,182,185,188,191,194,197,200],{"class":41,"line":181},8,[39,183,184],{"class":64},"  length",[39,186,187],{"class":67},"(",[39,189,190],{"class":64},"name",[39,192,193],{"class":67},") ",[39,195,196],{"class":64},"AS",[39,198,199],{"class":67}," name_len,               ",[39,201,202],{"class":45},"-- function call + alias\n",[39,204,206,210,213,215],{"class":41,"line":205},9,[39,207,209],{"class":208},"snvgF","  upper",[39,211,212],{"class":67},"(city) ",[39,214,196],{"class":64},[39,216,217],{"class":67}," city_upper\n",[39,219,221,223],{"class":41,"line":220},10,[39,222,74],{"class":64},[39,224,153],{"class":67},[39,226,228],{"class":41,"line":227},11,[39,229,159],{"emptyLinePlaceholder":158},[39,231,233],{"class":41,"line":232},12,[39,234,235],{"class":45},"-- Constants and computations\n",[39,237,239],{"class":41,"line":238},13,[39,240,169],{"class":64},[39,242,244,246],{"class":41,"line":243},14,[39,245,175],{"class":64},[39,247,178],{"class":67},[39,249,251,255,258,261],{"class":41,"line":250},15,[39,252,254],{"class":253},"sJ6F3","  'US'",[39,256,257],{"class":64}," AS",[39,259,260],{"class":67}," country,                        ",[39,262,263],{"class":45},"-- literal column\n",[39,265,267,270,273,276,278,281],{"class":41,"line":266},16,[39,268,269],{"class":208},"  2024",[39,271,272],{"class":64}," -",[39,274,275],{"class":208}," 2000",[39,277,257],{"class":64},[39,279,280],{"class":67}," age_estimate             ",[39,282,283],{"class":45},"-- arithmetic on literals\n",[39,285,287,289],{"class":41,"line":286},17,[39,288,74],{"class":64},[39,290,153],{"class":67},[292,293,295,298],"h3",{"id":294},"select-the-production-anti-pattern",[22,296,297],{},"SELECT *"," — the production anti-pattern",[27,300,301],{"language":29},[31,302,304],{"className":33,"code":303,"language":29,"meta":35,"style":35},"-- ❌ WRONG: SELECT * in production application code\nSELECT * FROM customers;\n\n-- ✅ RIGHT: explicit column list\nSELECT id, name, city, created_at FROM customers;\n",[22,305,306,311,323,327,332],{"__ignoreMap":35},[39,307,308],{"class":41,"line":42},[39,309,310],{"class":45},"-- ❌ WRONG: SELECT * in production application code\n",[39,312,313,315,318,321],{"class":41,"line":49},[39,314,24],{"class":64},[39,316,317],{"class":64}," *",[39,319,320],{"class":64}," FROM",[39,322,153],{"class":67},[39,324,325],{"class":41,"line":55},[39,326,159],{"emptyLinePlaceholder":158},[39,328,329],{"class":41,"line":61},[39,330,331],{"class":45},"-- ✅ RIGHT: explicit column list\n",[39,333,334,336,339,341,344,346],{"class":41,"line":71},[39,335,24],{"class":64},[39,337,338],{"class":67}," id, ",[39,340,190],{"class":64},[39,342,343],{"class":67},", city, created_at ",[39,345,74],{"class":64},[39,347,153],{"class":67},[19,349,350,352],{},[22,351,297],{}," is fine for quick exploration but an anti-pattern in production code:",[88,354,355,358,364],{},[91,356,357],{},"It returns columns you may not need, wasting I\u002FO, memory, and network bandwidth — especially with wide tables (50+ columns) or large TEXT\u002FJSONB columns.",[91,359,360,361,363],{},"If the table schema changes (a column is added), ",[22,362,297],{}," silently returns the new column, which can break applications expecting a fixed shape (ORM mapping errors, JSON deserialization failures).",[91,365,366,367,370,371,374,375,378,379,382,383,385],{},"It prevents the planner from using ",[97,368,369],{},"covering indexes"," — indexes that include all needed columns. If you only need ",[22,372,373],{},"name, city"," and there's an index on ",[22,376,377],{},"(name, city)",", ",[22,380,381],{},"SELECT name, city"," can be served entirely from the index (index-only scan, no table visit). ",[22,384,297],{}," forces a heap visit for every row.",[19,387,388],{},"Always list columns explicitly in application queries.",[14,390,392],{"id":391},"aliasing-columns","Aliasing Columns",[27,394,395],{"language":29},[31,396,398],{"className":33,"code":397,"language":29,"meta":35,"style":35},"-- AS is optional but improves readability — always use it in production code\nSELECT amount * 1.08 AS total_with_tax\nFROM orders;\n\n-- Quoted alias when it contains spaces or mixed case\nSELECT amount * 1.08 AS \"Total With Tax\"   -- quoted: preserves case and spaces\nFROM orders;\n\n-- Alias scoping: visible in ORDER BY (runs after SELECT), NOT in WHERE (runs before)\nSELECT amount * 1.08 AS taxed\nFROM orders\n-- WHERE taxed > 100;          -- ❌ ERROR: column \"taxed\" does not exist\nORDER BY taxed DESC;           -- ✅ works — ORDER BY runs after SELECT\n",[22,399,400,405,429,436,440,445,467,473,477,482,501,508,513],{"__ignoreMap":35},[39,401,402],{"class":41,"line":42},[39,403,404],{"class":45},"-- AS is optional but improves readability — always use it in production code\n",[39,406,407,409,412,415,418,421,424,426],{"class":41,"line":49},[39,408,24],{"class":64},[39,410,411],{"class":67}," amount ",[39,413,414],{"class":64},"*",[39,416,417],{"class":208}," 1",[39,419,420],{"class":67},".",[39,422,423],{"class":208},"08",[39,425,257],{"class":64},[39,427,428],{"class":67}," total_with_tax\n",[39,430,431,433],{"class":41,"line":55},[39,432,74],{"class":64},[39,434,435],{"class":67}," orders;\n",[39,437,438],{"class":41,"line":61},[39,439,159],{"emptyLinePlaceholder":158},[39,441,442],{"class":41,"line":71},[39,443,444],{"class":45},"-- Quoted alias when it contains spaces or mixed case\n",[39,446,447,449,451,453,455,457,459,461,464],{"class":41,"line":80},[39,448,24],{"class":64},[39,450,411],{"class":67},[39,452,414],{"class":64},[39,454,417],{"class":208},[39,456,420],{"class":67},[39,458,423],{"class":208},[39,460,257],{"class":64},[39,462,463],{"class":253}," \"Total With Tax\"",[39,465,466],{"class":45},"   -- quoted: preserves case and spaces\n",[39,468,469,471],{"class":41,"line":172},[39,470,74],{"class":64},[39,472,435],{"class":67},[39,474,475],{"class":41,"line":181},[39,476,159],{"emptyLinePlaceholder":158},[39,478,479],{"class":41,"line":205},[39,480,481],{"class":45},"-- Alias scoping: visible in ORDER BY (runs after SELECT), NOT in WHERE (runs before)\n",[39,483,484,486,488,490,492,494,496,498],{"class":41,"line":220},[39,485,24],{"class":64},[39,487,411],{"class":67},[39,489,414],{"class":64},[39,491,417],{"class":208},[39,493,420],{"class":67},[39,495,423],{"class":208},[39,497,257],{"class":64},[39,499,500],{"class":67}," taxed\n",[39,502,503,505],{"class":41,"line":227},[39,504,74],{"class":64},[39,506,507],{"class":67}," orders\n",[39,509,510],{"class":41,"line":232},[39,511,512],{"class":45},"-- WHERE taxed > 100;          -- ❌ ERROR: column \"taxed\" does not exist\n",[39,514,515,518,521,524,527],{"class":41,"line":238},[39,516,517],{"class":64},"ORDER BY",[39,519,520],{"class":67}," taxed ",[39,522,523],{"class":64},"DESC",[39,525,526],{"class":67},";           ",[39,528,529],{"class":45},"-- ✅ works — ORDER BY runs after SELECT\n",[19,531,532,533,535,536,540,541,378,543,420],{},"Aliases are purely display labels — they don't change data. The scoping rule follows the logical evaluation order (see Chapter 01): aliases are created in ",[22,534,24],{}," (step 5) and are visible only in clauses that run ",[537,538,539],"em",{},"after"," it: ",[22,542,517],{},[22,544,545],{},"LIMIT",[14,547,549],{"id":548},"distinct","DISTINCT",[27,551,552],{"language":29},[31,553,555],{"className":33,"code":554,"language":29,"meta":35,"style":35},"-- Unique cities — DISTINCT applies to the entire row, not individual columns\nSELECT DISTINCT city FROM customers;\n\n-- Distinct combinations — returns unique (city, name) pairs, not unique cities + unique names\nSELECT DISTINCT city, name FROM customers;\n",[22,556,557,562,574,578,583],{"__ignoreMap":35},[39,558,559],{"class":41,"line":42},[39,560,561],{"class":45},"-- Unique cities — DISTINCT applies to the entire row, not individual columns\n",[39,563,564,567,570,572],{"class":41,"line":49},[39,565,566],{"class":64},"SELECT DISTINCT",[39,568,569],{"class":67}," city ",[39,571,74],{"class":64},[39,573,153],{"class":67},[39,575,576],{"class":41,"line":55},[39,577,159],{"emptyLinePlaceholder":158},[39,579,580],{"class":41,"line":61},[39,581,582],{"class":45},"-- Distinct combinations — returns unique (city, name) pairs, not unique cities + unique names\n",[39,584,585,587,590,592,594],{"class":41,"line":71},[39,586,566],{"class":64},[39,588,589],{"class":67}," city, ",[39,591,190],{"class":64},[39,593,320],{"class":64},[39,595,153],{"class":67},[19,597,598,600,601,604,605,608],{},[22,599,549],{}," applies to the ",[537,602,603],{},"entire row",", not individual columns. ",[22,606,607],{},"SELECT DISTINCT city, name"," returns unique (city, name) pairs.",[292,610,612,615],{"id":611},"distinct-on-postgresql-specific",[22,613,614],{},"DISTINCT ON"," (PostgreSQL-specific)",[27,617,618],{"language":29},[31,619,621],{"className":33,"code":620,"language":29,"meta":35,"style":35},"-- The largest order per customer — \"greatest-N-per-group\" in one query.\n-- DISTINCT ON (customer_id) keeps the first row per customer_id group.\n-- ORDER BY must start with the DISTINCT ON columns, then the ranking within each group.\nSELECT DISTINCT ON (customer_id)\n  customer_id, id, amount\nFROM orders\nORDER BY customer_id, amount DESC;   -- within each customer, sort by amount DESC → keep the biggest\n",[22,622,623,628,633,638,648,653,659],{"__ignoreMap":35},[39,624,625],{"class":41,"line":42},[39,626,627],{"class":45},"-- The largest order per customer — \"greatest-N-per-group\" in one query.\n",[39,629,630],{"class":41,"line":49},[39,631,632],{"class":45},"-- DISTINCT ON (customer_id) keeps the first row per customer_id group.\n",[39,634,635],{"class":41,"line":55},[39,636,637],{"class":45},"-- ORDER BY must start with the DISTINCT ON columns, then the ranking within each group.\n",[39,639,640,642,645],{"class":41,"line":61},[39,641,566],{"class":64},[39,643,644],{"class":64}," ON",[39,646,647],{"class":67}," (customer_id)\n",[39,649,650],{"class":41,"line":71},[39,651,652],{"class":67},"  customer_id, id, amount\n",[39,654,655,657],{"class":41,"line":80},[39,656,74],{"class":64},[39,658,507],{"class":67},[39,660,661,663,666,668,671],{"class":41,"line":172},[39,662,517],{"class":64},[39,664,665],{"class":67}," customer_id, amount ",[39,667,523],{"class":64},[39,669,670],{"class":67},";   ",[39,672,673],{"class":45},"-- within each customer, sort by amount DESC → keep the biggest\n",[19,675,676],{},"This is a concise alternative to window functions (Chapter 09) for \"greatest-N-per-group\" queries.",[14,678,680],{"id":679},"where-filtering-rows","WHERE — Filtering Rows",[19,682,683,685,686,689,690,693,694,697],{},[22,684,83],{}," keeps rows where the condition is ",[22,687,688],{},"TRUE",". Rows where the condition is ",[22,691,692],{},"FALSE"," or ",[22,695,696],{},"UNKNOWN"," (NULL-related) are discarded.",[292,699,701],{"id":700},"comparison-operators","Comparison Operators",[703,704,705,721],"table",{},[706,707,708],"thead",{},[709,710,711,715,718],"tr",{},[712,713,714],"th",{},"Operator",[712,716,717],{},"Meaning",[712,719,720],{},"Notes",[722,723,724,744,765,786,802,817,829,842,863,877,894],"tbody",{},[709,725,726,732,735],{},[727,728,729],"td",{},[22,730,731],{},"=",[727,733,734],{},"Equal",[727,736,737,738,740,741,420],{},"Single ",[22,739,731],{},", not ",[22,742,743],{},"==",[709,745,746,754,757],{},[727,747,748,693,751],{},[22,749,750],{},"\u003C>",[22,752,753],{},"!=",[727,755,756],{},"Not equal",[727,758,759,761,762,764],{},[22,760,750],{}," is ANSI standard; ",[22,763,753],{}," widely supported.",[709,766,767,781,784],{},[727,768,769,378,772,378,775,378,778],{},[22,770,771],{},"\u003C",[22,773,774],{},"\u003C=",[22,776,777],{},">",[22,779,780],{},">=",[727,782,783],{},"Ordering",[727,785],{},[709,787,788,793,796],{},[727,789,790],{},[22,791,792],{},"BETWEEN x AND y",[727,794,795],{},"Inclusive range",[727,797,798,801],{},[22,799,800],{},">= x AND \u003C= y"," — both ends inclusive.",[709,803,804,809,812],{},[727,805,806],{},[22,807,808],{},"NOT BETWEEN x AND y",[727,810,811],{},"Outside range",[727,813,814,420],{},[22,815,816],{},"x > col OR col > y",[709,818,819,824,827],{},[727,820,821],{},[22,822,823],{},"IN (a, b, c)",[727,825,826],{},"Equal to any listed value",[727,828],{},[709,830,831,836,839],{},[727,832,833],{},[22,834,835],{},"NOT IN (a, b, c)",[727,837,838],{},"Not equal to any listed value",[727,840,841],{},"⚠️ NULL poison — see below.",[709,843,844,849,860],{},[727,845,846],{},[22,847,848],{},"LIKE",[727,850,851,852,855,856,859],{},"Pattern: ",[22,853,854],{},"%"," = any chars, ",[22,857,858],{},"_"," = one char",[727,861,862],{},"Case-sensitive in PostgreSQL.",[709,864,865,871,874],{},[727,866,867,870],{},[22,868,869],{},"ILIKE"," (PostgreSQL)",[727,872,873],{},"Case-insensitive LIKE",[727,875,876],{},"PostgreSQL extension.",[709,878,879,888,891],{},[727,880,881,884,885],{},[22,882,883],{},"IS NULL"," \u002F ",[22,886,887],{},"IS NOT NULL",[727,889,890],{},"NULL tests",[727,892,893],{},"The only correct way to test for NULL.",[709,895,896,901,904],{},[727,897,898],{},[22,899,900],{},"IS [NOT] DISTINCT FROM",[727,902,903],{},"NULL-safe equality",[727,905,906,907,910],{},"Treats ",[22,908,909],{},"NULL = NULL"," as true.",[27,912,913],{"language":29},[31,914,916],{"className":33,"code":915,"language":29,"meta":35,"style":35},"SELECT name, city FROM customers WHERE city = 'NYC';\nSELECT name FROM customers WHERE name \u003C> 'Alice';\nSELECT * FROM orders WHERE amount >= 100;\nSELECT * FROM orders WHERE amount BETWEEN 10 AND 100;   -- 10 \u003C= amount \u003C= 100 (inclusive)\nSELECT * FROM orders WHERE customer_id IN (1, 3);\nSELECT * FROM customers WHERE name LIKE 'A%';            -- starts with 'A'\nSELECT * FROM customers WHERE name LIKE '_a%';           -- second char is 'a'\nSELECT * FROM customers WHERE city IS NULL;              -- correct NULL test\nSELECT * FROM customers WHERE city IS DISTINCT FROM 'NYC'; -- NULL-safe \"not NYC\" — includes NULL cities\n",[22,917,918,943,965,987,1017,1049,1075,1099,1125],{"__ignoreMap":35},[39,919,920,922,924,926,928,931,933,935,937,940],{"class":41,"line":42},[39,921,24],{"class":64},[39,923,145],{"class":64},[39,925,148],{"class":67},[39,927,74],{"class":64},[39,929,930],{"class":67}," customers ",[39,932,83],{"class":64},[39,934,569],{"class":67},[39,936,731],{"class":64},[39,938,939],{"class":253}," 'NYC'",[39,941,942],{"class":67},";\n",[39,944,945,947,949,951,953,955,957,960,963],{"class":41,"line":49},[39,946,24],{"class":64},[39,948,145],{"class":64},[39,950,320],{"class":64},[39,952,930],{"class":67},[39,954,83],{"class":64},[39,956,145],{"class":64},[39,958,959],{"class":64}," \u003C>",[39,961,962],{"class":253}," 'Alice'",[39,964,942],{"class":67},[39,966,967,969,971,973,976,978,980,982,985],{"class":41,"line":55},[39,968,24],{"class":64},[39,970,317],{"class":64},[39,972,320],{"class":64},[39,974,975],{"class":67}," orders ",[39,977,83],{"class":64},[39,979,411],{"class":67},[39,981,780],{"class":64},[39,983,984],{"class":208}," 100",[39,986,942],{"class":67},[39,988,989,991,993,995,997,999,1001,1004,1007,1010,1012,1014],{"class":41,"line":61},[39,990,24],{"class":64},[39,992,317],{"class":64},[39,994,320],{"class":64},[39,996,975],{"class":67},[39,998,83],{"class":64},[39,1000,411],{"class":67},[39,1002,1003],{"class":64},"BETWEEN",[39,1005,1006],{"class":208}," 10",[39,1008,1009],{"class":64}," AND",[39,1011,984],{"class":208},[39,1013,670],{"class":67},[39,1015,1016],{"class":45},"-- 10 \u003C= amount \u003C= 100 (inclusive)\n",[39,1018,1019,1021,1023,1025,1027,1029,1032,1035,1038,1041,1043,1046],{"class":41,"line":71},[39,1020,24],{"class":64},[39,1022,317],{"class":64},[39,1024,320],{"class":64},[39,1026,975],{"class":67},[39,1028,83],{"class":64},[39,1030,1031],{"class":67}," customer_id ",[39,1033,1034],{"class":64},"IN",[39,1036,1037],{"class":67}," (",[39,1039,1040],{"class":208},"1",[39,1042,378],{"class":67},[39,1044,1045],{"class":208},"3",[39,1047,1048],{"class":67},");\n",[39,1050,1051,1053,1055,1057,1059,1061,1063,1066,1069,1072],{"class":41,"line":80},[39,1052,24],{"class":64},[39,1054,317],{"class":64},[39,1056,320],{"class":64},[39,1058,930],{"class":67},[39,1060,83],{"class":64},[39,1062,145],{"class":64},[39,1064,1065],{"class":64}," LIKE",[39,1067,1068],{"class":253}," 'A%'",[39,1070,1071],{"class":67},";            ",[39,1073,1074],{"class":45},"-- starts with 'A'\n",[39,1076,1077,1079,1081,1083,1085,1087,1089,1091,1094,1096],{"class":41,"line":172},[39,1078,24],{"class":64},[39,1080,317],{"class":64},[39,1082,320],{"class":64},[39,1084,930],{"class":67},[39,1086,83],{"class":64},[39,1088,145],{"class":64},[39,1090,1065],{"class":64},[39,1092,1093],{"class":253}," '_a%'",[39,1095,526],{"class":67},[39,1097,1098],{"class":45},"-- second char is 'a'\n",[39,1100,1101,1103,1105,1107,1109,1111,1113,1116,1119,1122],{"class":41,"line":181},[39,1102,24],{"class":64},[39,1104,317],{"class":64},[39,1106,320],{"class":64},[39,1108,930],{"class":67},[39,1110,83],{"class":64},[39,1112,569],{"class":67},[39,1114,1115],{"class":64},"IS",[39,1117,1118],{"class":64}," NULL",[39,1120,1121],{"class":67},";              ",[39,1123,1124],{"class":45},"-- correct NULL test\n",[39,1126,1127,1129,1131,1133,1135,1137,1139,1141,1144,1146,1148,1151],{"class":41,"line":205},[39,1128,24],{"class":64},[39,1130,317],{"class":64},[39,1132,320],{"class":64},[39,1134,930],{"class":67},[39,1136,83],{"class":64},[39,1138,569],{"class":67},[39,1140,1115],{"class":64},[39,1142,1143],{"class":64}," DISTINCT",[39,1145,320],{"class":64},[39,1147,939],{"class":253},[39,1149,1150],{"class":67},"; ",[39,1152,1153],{"class":45},"-- NULL-safe \"not NYC\" — includes NULL cities\n",[292,1155,1157],{"id":1156},"logical-operators","Logical Operators",[703,1159,1160,1168],{},[706,1161,1162],{},[709,1163,1164,1166],{},[712,1165,714],{},[712,1167,717],{},[722,1169,1170,1180,1190],{},[709,1171,1172,1177],{},[727,1173,1174],{},[22,1175,1176],{},"AND",[727,1178,1179],{},"Both conditions true.",[709,1181,1182,1187],{},[727,1183,1184],{},[22,1185,1186],{},"OR",[727,1188,1189],{},"Either condition true.",[709,1191,1192,1197],{},[727,1193,1194],{},[22,1195,1196],{},"NOT",[727,1198,1199],{},"Negates a condition.",[19,1201,1202,1204,1205,1207,1208,1211,1212,1215],{},[22,1203,1176],{}," binds tighter than ",[22,1206,1186],{},", so ",[22,1209,1210],{},"A OR B AND C"," is ",[22,1213,1214],{},"A OR (B AND C)",". Always use parentheses:",[27,1217,1218],{"language":29},[31,1219,1221],{"className":33,"code":1220,"language":29,"meta":35,"style":35},"-- ❌ Unclear — relies on remembering AND > OR precedence\nSELECT * FROM orders\nWHERE customer_id = 1 OR customer_id = 2 AND amount > 100;\n\n-- ✅ Explicit — intent is obvious\nSELECT * FROM orders\nWHERE customer_id = 1 OR (customer_id = 2 AND amount > 100);\n\n-- ✅ Different intent entirely\nSELECT * FROM orders\nWHERE (customer_id = 1 OR customer_id = 2) AND amount > 100;\n",[22,1222,1223,1228,1238,1268,1272,1277,1287,1316,1320,1325,1335],{"__ignoreMap":35},[39,1224,1225],{"class":41,"line":42},[39,1226,1227],{"class":45},"-- ❌ Unclear — relies on remembering AND > OR precedence\n",[39,1229,1230,1232,1234,1236],{"class":41,"line":49},[39,1231,24],{"class":64},[39,1233,317],{"class":64},[39,1235,320],{"class":64},[39,1237,507],{"class":67},[39,1239,1240,1242,1244,1246,1248,1251,1253,1255,1258,1260,1262,1264,1266],{"class":41,"line":55},[39,1241,83],{"class":64},[39,1243,1031],{"class":67},[39,1245,731],{"class":64},[39,1247,417],{"class":208},[39,1249,1250],{"class":64}," OR",[39,1252,1031],{"class":67},[39,1254,731],{"class":64},[39,1256,1257],{"class":208}," 2",[39,1259,1009],{"class":64},[39,1261,411],{"class":67},[39,1263,777],{"class":64},[39,1265,984],{"class":208},[39,1267,942],{"class":67},[39,1269,1270],{"class":41,"line":61},[39,1271,159],{"emptyLinePlaceholder":158},[39,1273,1274],{"class":41,"line":71},[39,1275,1276],{"class":45},"-- ✅ Explicit — intent is obvious\n",[39,1278,1279,1281,1283,1285],{"class":41,"line":80},[39,1280,24],{"class":64},[39,1282,317],{"class":64},[39,1284,320],{"class":64},[39,1286,507],{"class":67},[39,1288,1289,1291,1293,1295,1297,1299,1302,1304,1306,1308,1310,1312,1314],{"class":41,"line":172},[39,1290,83],{"class":64},[39,1292,1031],{"class":67},[39,1294,731],{"class":64},[39,1296,417],{"class":208},[39,1298,1250],{"class":64},[39,1300,1301],{"class":67}," (customer_id ",[39,1303,731],{"class":64},[39,1305,1257],{"class":208},[39,1307,1009],{"class":64},[39,1309,411],{"class":67},[39,1311,777],{"class":64},[39,1313,984],{"class":208},[39,1315,1048],{"class":67},[39,1317,1318],{"class":41,"line":181},[39,1319,159],{"emptyLinePlaceholder":158},[39,1321,1322],{"class":41,"line":205},[39,1323,1324],{"class":45},"-- ✅ Different intent entirely\n",[39,1326,1327,1329,1331,1333],{"class":41,"line":220},[39,1328,24],{"class":64},[39,1330,317],{"class":64},[39,1332,320],{"class":64},[39,1334,507],{"class":67},[39,1336,1337,1339,1341,1343,1345,1347,1349,1351,1353,1355,1357,1359,1361,1363],{"class":41,"line":227},[39,1338,83],{"class":64},[39,1340,1301],{"class":67},[39,1342,731],{"class":64},[39,1344,417],{"class":208},[39,1346,1250],{"class":64},[39,1348,1031],{"class":67},[39,1350,731],{"class":64},[39,1352,1257],{"class":208},[39,1354,193],{"class":67},[39,1356,1176],{"class":64},[39,1358,411],{"class":67},[39,1360,777],{"class":64},[39,1362,984],{"class":208},[39,1364,942],{"class":67},[14,1366,1368],{"id":1367},"like-patterns","LIKE Patterns",[703,1370,1371,1381],{},[706,1372,1373],{},[709,1374,1375,1378],{},[712,1376,1377],{},"Pattern",[712,1379,1380],{},"Matches",[722,1382,1383,1396,1409,1421,1433],{},[709,1384,1385,1390],{},[727,1386,1387],{},[22,1388,1389],{},"'A%'",[727,1391,1392,1393,420],{},"Anything starting with ",[22,1394,1395],{},"A",[709,1397,1398,1403],{},[727,1399,1400],{},[22,1401,1402],{},"'%a'",[727,1404,1405,1406,420],{},"Anything ending with ",[22,1407,1408],{},"a",[709,1410,1411,1416],{},[727,1412,1413],{},[22,1414,1415],{},"'%a%'",[727,1417,1418,1419,420],{},"Anything containing ",[22,1420,1408],{},[709,1422,1423,1428],{},[727,1424,1425],{},[22,1426,1427],{},"'_a%'",[727,1429,1430,1431,420],{},"Second character is ",[22,1432,1408],{},[709,1434,1435,1440],{},[727,1436,1437],{},[22,1438,1439],{},"'A__e'",[727,1441,1442,1443,1445,1446,420],{},"Four chars: ",[22,1444,1395],{},", any, any, ",[22,1447,1448],{},"e",[27,1450,1451],{"language":29},[31,1452,1454],{"className":33,"code":1453,"language":29,"meta":35,"style":35},"-- Escape literal wildcards using ESCAPE clause\nSELECT * FROM users WHERE name LIKE '%\\_%' ESCAPE '\\';   -- match literal underscore in name\n",[22,1455,1456,1461],{"__ignoreMap":35},[39,1457,1458],{"class":41,"line":42},[39,1459,1460],{"class":45},"-- Escape literal wildcards using ESCAPE clause\n",[39,1462,1463,1465,1467,1469,1472,1474,1476,1478,1481,1484,1487,1489],{"class":41,"line":49},[39,1464,24],{"class":64},[39,1466,317],{"class":64},[39,1468,320],{"class":64},[39,1470,1471],{"class":67}," users ",[39,1473,83],{"class":64},[39,1475,145],{"class":64},[39,1477,1065],{"class":64},[39,1479,1480],{"class":253}," '%\\_%'",[39,1482,1483],{"class":67}," ESCAPE ",[39,1485,1486],{"class":253},"'\\'",[39,1488,670],{"class":67},[39,1490,1491],{"class":45},"-- match literal underscore in name\n",[19,1493,1494,1497,1498,378,1501,1504,1505,1508],{},[97,1495,1496],{},"Index implications",": leading wildcards (",[22,1499,1500],{},"'%foo'",[22,1502,1503],{},"'%foo%'",") defeat B-tree indexes — the database must scan every row. For high-volume substring search, use full-text search (Chapter 20) or trigram indexes (",[22,1506,1507],{},"pg_trgm","):",[27,1510,1511],{"language":29},[31,1512,1514],{"className":33,"code":1513,"language":29,"meta":35,"style":35},"-- pg_trgm trigram index supports %foo% with index acceleration\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE INDEX idx_users_name_trgm ON users USING gin (name gin_trgm_ops);\n\nSELECT * FROM users WHERE name LIKE '%alice%';   -- now uses the GIN trigram index\n",[22,1515,1516,1521,1541,1567,1571],{"__ignoreMap":35},[39,1517,1518],{"class":41,"line":42},[39,1519,1520],{"class":45},"-- pg_trgm trigram index supports %foo% with index acceleration\n",[39,1522,1523,1526,1529,1532,1535,1538],{"class":41,"line":49},[39,1524,1525],{"class":64},"CREATE",[39,1527,1528],{"class":67}," EXTENSION ",[39,1530,1531],{"class":64},"IF",[39,1533,1534],{"class":64}," NOT",[39,1536,1537],{"class":64}," EXISTS",[39,1539,1540],{"class":67}," pg_trgm;\n",[39,1542,1543,1545,1548,1552,1554,1556,1559,1562,1564],{"class":41,"line":55},[39,1544,1525],{"class":64},[39,1546,1547],{"class":64}," INDEX",[39,1549,1551],{"class":1550},"sIsaT"," idx_users_name_trgm",[39,1553,644],{"class":64},[39,1555,1471],{"class":67},[39,1557,1558],{"class":64},"USING",[39,1560,1561],{"class":67}," gin (",[39,1563,190],{"class":64},[39,1565,1566],{"class":67}," gin_trgm_ops);\n",[39,1568,1569],{"class":41,"line":61},[39,1570,159],{"emptyLinePlaceholder":158},[39,1572,1573,1575,1577,1579,1581,1583,1585,1587,1590,1592],{"class":41,"line":71},[39,1574,24],{"class":64},[39,1576,317],{"class":64},[39,1578,320],{"class":64},[39,1580,1471],{"class":67},[39,1582,83],{"class":64},[39,1584,145],{"class":64},[39,1586,1065],{"class":64},[39,1588,1589],{"class":253}," '%alice%'",[39,1591,670],{"class":67},[39,1593,1594],{"class":45},"-- now uses the GIN trigram index\n",[14,1596,1598],{"id":1597},"in-and-subqueries-vs-exists","IN and Subqueries vs EXISTS",[27,1600,1601],{"language":29},[31,1602,1604],{"className":33,"code":1603,"language":29,"meta":35,"style":35},"-- IN with a subquery — a \"semi-join\": keep left rows that have at least one match in the subquery\nSELECT * FROM customers\nWHERE id IN (SELECT customer_id FROM orders);\n\n-- EXISTS — same result, different execution strategy\nSELECT * FROM customers c\nWHERE EXISTS (\n  SELECT 1 FROM orders o WHERE o.customer_id = c.id   -- SELECT 1: we only care about existence, not values\n);\n\n-- NOT EXISTS — immune to NULL poisoning (unlike NOT IN)\nSELECT * FROM customers c\nWHERE NOT EXISTS (\n  SELECT 1 FROM orders o WHERE o.customer_id = c.id\n);\n",[22,1605,1606,1611,1622,1642,1646,1651,1662,1671,1707,1711,1715,1720,1730,1740,1767],{"__ignoreMap":35},[39,1607,1608],{"class":41,"line":42},[39,1609,1610],{"class":45},"-- IN with a subquery — a \"semi-join\": keep left rows that have at least one match in the subquery\n",[39,1612,1613,1615,1617,1619],{"class":41,"line":49},[39,1614,24],{"class":64},[39,1616,317],{"class":64},[39,1618,320],{"class":64},[39,1620,1621],{"class":67}," customers\n",[39,1623,1624,1626,1629,1631,1633,1635,1637,1639],{"class":41,"line":55},[39,1625,83],{"class":64},[39,1627,1628],{"class":67}," id ",[39,1630,1034],{"class":64},[39,1632,1037],{"class":67},[39,1634,24],{"class":64},[39,1636,1031],{"class":67},[39,1638,74],{"class":64},[39,1640,1641],{"class":67}," orders);\n",[39,1643,1644],{"class":41,"line":61},[39,1645,159],{"emptyLinePlaceholder":158},[39,1647,1648],{"class":41,"line":71},[39,1649,1650],{"class":45},"-- EXISTS — same result, different execution strategy\n",[39,1652,1653,1655,1657,1659],{"class":41,"line":80},[39,1654,24],{"class":64},[39,1656,317],{"class":64},[39,1658,320],{"class":64},[39,1660,1661],{"class":67}," customers c\n",[39,1663,1664,1666,1668],{"class":41,"line":172},[39,1665,83],{"class":64},[39,1667,1537],{"class":64},[39,1669,1670],{"class":67}," (\n",[39,1672,1673,1676,1678,1680,1683,1685,1688,1690,1693,1696,1699,1701,1704],{"class":41,"line":181},[39,1674,1675],{"class":64},"  SELECT",[39,1677,417],{"class":208},[39,1679,320],{"class":64},[39,1681,1682],{"class":67}," orders o ",[39,1684,83],{"class":64},[39,1686,1687],{"class":208}," o",[39,1689,420],{"class":67},[39,1691,1692],{"class":208},"customer_id",[39,1694,1695],{"class":64}," =",[39,1697,1698],{"class":208}," c",[39,1700,420],{"class":67},[39,1702,1703],{"class":208},"id",[39,1705,1706],{"class":45},"   -- SELECT 1: we only care about existence, not values\n",[39,1708,1709],{"class":41,"line":205},[39,1710,1048],{"class":67},[39,1712,1713],{"class":41,"line":220},[39,1714,159],{"emptyLinePlaceholder":158},[39,1716,1717],{"class":41,"line":227},[39,1718,1719],{"class":45},"-- NOT EXISTS — immune to NULL poisoning (unlike NOT IN)\n",[39,1721,1722,1724,1726,1728],{"class":41,"line":232},[39,1723,24],{"class":64},[39,1725,317],{"class":64},[39,1727,320],{"class":64},[39,1729,1661],{"class":67},[39,1731,1732,1734,1736,1738],{"class":41,"line":238},[39,1733,83],{"class":64},[39,1735,1534],{"class":64},[39,1737,1537],{"class":64},[39,1739,1670],{"class":67},[39,1741,1742,1744,1746,1748,1750,1752,1754,1756,1758,1760,1762,1764],{"class":41,"line":243},[39,1743,1675],{"class":64},[39,1745,417],{"class":208},[39,1747,320],{"class":64},[39,1749,1682],{"class":67},[39,1751,83],{"class":64},[39,1753,1687],{"class":208},[39,1755,420],{"class":67},[39,1757,1692],{"class":208},[39,1759,1695],{"class":64},[39,1761,1698],{"class":208},[39,1763,420],{"class":67},[39,1765,1766],{"class":208},"id\n",[39,1768,1769],{"class":41,"line":250},[39,1770,1048],{"class":67},[19,1772,1773,1776,1777,1779],{},[22,1774,1775],{},"EXISTS"," short-circuits on the first match — it doesn't need to materialize the full subquery result. ",[22,1778,1034],{}," may build the full list first. The difference is most visible when the subquery can produce NULLs (see the NULL poison gotcha below).",[14,1781,1783],{"id":1782},"null-three-valued-logic-preview","NULL: Three-Valued Logic (preview)",[19,1785,1786,1789,1790,1792,1793,1795,1796,740,1798,1508],{},[22,1787,1788],{},"NULL"," is not a value — it's the absence of one. Comparisons with ",[22,1791,1788],{}," produce ",[22,1794,696],{}," (not ",[22,1797,688],{},[22,1799,692],{},[27,1801,1802],{"language":29},[31,1803,1805],{"className":33,"code":1804,"language":29,"meta":35,"style":35},"SELECT * FROM customers WHERE city = NULL;     -- ❌ returns nothing — NULL = NULL is UNKNOWN\nSELECT * FROM customers WHERE city IS NULL;    -- ✅ correct NULL test\nSELECT * FROM customers WHERE city != 'NYC';   -- ❌ excludes NULLs too! NULL != 'NYC' is UNKNOWN\n",[22,1806,1807,1831,1855],{"__ignoreMap":35},[39,1808,1809,1811,1813,1815,1817,1819,1821,1823,1825,1828],{"class":41,"line":42},[39,1810,24],{"class":64},[39,1812,317],{"class":64},[39,1814,320],{"class":64},[39,1816,930],{"class":67},[39,1818,83],{"class":64},[39,1820,569],{"class":67},[39,1822,731],{"class":64},[39,1824,1118],{"class":64},[39,1826,1827],{"class":67},";     ",[39,1829,1830],{"class":45},"-- ❌ returns nothing — NULL = NULL is UNKNOWN\n",[39,1832,1833,1835,1837,1839,1841,1843,1845,1847,1849,1852],{"class":41,"line":49},[39,1834,24],{"class":64},[39,1836,317],{"class":64},[39,1838,320],{"class":64},[39,1840,930],{"class":67},[39,1842,83],{"class":64},[39,1844,569],{"class":67},[39,1846,1115],{"class":64},[39,1848,1118],{"class":64},[39,1850,1851],{"class":67},";    ",[39,1853,1854],{"class":45},"-- ✅ correct NULL test\n",[39,1856,1857,1859,1861,1863,1865,1867,1869,1871,1873,1875],{"class":41,"line":55},[39,1858,24],{"class":64},[39,1860,317],{"class":64},[39,1862,320],{"class":64},[39,1864,930],{"class":67},[39,1866,83],{"class":64},[39,1868,569],{"class":67},[39,1870,753],{"class":64},[39,1872,939],{"class":253},[39,1874,670],{"class":67},[39,1876,1877],{"class":45},"-- ❌ excludes NULLs too! NULL != 'NYC' is UNKNOWN\n",[703,1879,1880,1890],{},[706,1881,1882],{},[709,1883,1884,1887],{},[712,1885,1886],{},"Expression",[712,1888,1889],{},"Result",[722,1891,1892,1900,1909,1918,1927,1936,1945,1954],{},[709,1893,1894,1898],{},[727,1895,1896],{},[22,1897,909],{},[727,1899,696],{},[709,1901,1902,1907],{},[727,1903,1904],{},[22,1905,1906],{},"NULL != 'NYC'",[727,1908,696],{},[709,1910,1911,1916],{},[727,1912,1913],{},[22,1914,1915],{},"NULL IS NULL",[727,1917,688],{},[709,1919,1920,1925],{},[727,1921,1922],{},[22,1923,1924],{},"NULL IS NOT NULL",[727,1926,692],{},[709,1928,1929,1934],{},[727,1930,1931],{},[22,1932,1933],{},"NULL + 1",[727,1935,1788],{},[709,1937,1938,1943],{},[727,1939,1940],{},[22,1941,1942],{},"UNKNOWN AND TRUE",[727,1944,696],{},[709,1946,1947,1952],{},[727,1948,1949],{},[22,1950,1951],{},"UNKNOWN AND FALSE",[727,1953,692],{},[709,1955,1956,1961],{},[727,1957,1958],{},[22,1959,1960],{},"UNKNOWN OR TRUE",[727,1962,688],{},[19,1964,1965,1967,1968,1970,1971,1973],{},[22,1966,83],{}," only keeps rows where the condition is ",[22,1969,688],{},". ",[22,1972,696],{}," is treated as \"not true\" → row excluded.",[14,1975,1977],{"id":1976},"null-safe-operators","NULL-Safe Operators",[27,1979,1980],{"language":29},[31,1981,1983],{"className":33,"code":1982,"language":29,"meta":35,"style":35},"-- IS DISTINCT FROM — NULL-safe \"not equal\": treats NULL = NULL as equal (not distinct)\nSELECT * FROM customers WHERE city IS DISTINCT FROM 'NYC';\n-- Returns rows where city != 'NYC' OR city IS NULL — includes unknown cities\n\n-- IS NOT DISTINCT FROM — NULL-safe \"equal\": NULL IS NOT DISTINCT FROM NULL → TRUE\nSELECT * FROM orders WHERE amount IS NOT DISTINCT FROM NULL;\n-- Equivalent to: WHERE amount IS NULL, but useful in generic\u002Fcomparison code\n",[22,1984,1985,1990,2014,2019,2023,2028,2054],{"__ignoreMap":35},[39,1986,1987],{"class":41,"line":42},[39,1988,1989],{"class":45},"-- IS DISTINCT FROM — NULL-safe \"not equal\": treats NULL = NULL as equal (not distinct)\n",[39,1991,1992,1994,1996,1998,2000,2002,2004,2006,2008,2010,2012],{"class":41,"line":49},[39,1993,24],{"class":64},[39,1995,317],{"class":64},[39,1997,320],{"class":64},[39,1999,930],{"class":67},[39,2001,83],{"class":64},[39,2003,569],{"class":67},[39,2005,1115],{"class":64},[39,2007,1143],{"class":64},[39,2009,320],{"class":64},[39,2011,939],{"class":253},[39,2013,942],{"class":67},[39,2015,2016],{"class":41,"line":55},[39,2017,2018],{"class":45},"-- Returns rows where city != 'NYC' OR city IS NULL — includes unknown cities\n",[39,2020,2021],{"class":41,"line":61},[39,2022,159],{"emptyLinePlaceholder":158},[39,2024,2025],{"class":41,"line":71},[39,2026,2027],{"class":45},"-- IS NOT DISTINCT FROM — NULL-safe \"equal\": NULL IS NOT DISTINCT FROM NULL → TRUE\n",[39,2029,2030,2032,2034,2036,2038,2040,2042,2044,2046,2048,2050,2052],{"class":41,"line":80},[39,2031,24],{"class":64},[39,2033,317],{"class":64},[39,2035,320],{"class":64},[39,2037,975],{"class":67},[39,2039,83],{"class":64},[39,2041,411],{"class":67},[39,2043,1115],{"class":64},[39,2045,1534],{"class":64},[39,2047,1143],{"class":64},[39,2049,320],{"class":64},[39,2051,1118],{"class":64},[39,2053,942],{"class":67},[39,2055,2056],{"class":41,"line":172},[39,2057,2058],{"class":45},"-- Equivalent to: WHERE amount IS NULL, but useful in generic\u002Fcomparison code\n",[19,2060,2061,2064,2065,2067],{},[22,2062,2063],{},"IS DISTINCT FROM"," is the cleanest way to say \"not equal, counting NULL as a real value.\" It's essential in ",[22,2066,83],{}," clauses where NULL is a meaningful \"other\" state.",[14,2069,2071],{"id":2070},"filtering-on-computed-columns","Filtering on Computed Columns",[19,2073,2074,2075,2077,2078,2080],{},"You can't use a ",[22,2076,24],{}," alias in ",[22,2079,83],{}," (it doesn't exist yet — see evaluation order). Repeat the expression or use a CTE:",[27,2082,2083],{"language":29},[31,2084,2086],{"className":33,"code":2085,"language":29,"meta":35,"style":35},"-- ❌ ERROR: alias \"taxed\" doesn't exist in WHERE (WHERE runs before SELECT)\nSELECT amount * 1.08 AS taxed FROM orders WHERE taxed > 100;\n\n-- ✅ Repeat the expression\nSELECT amount * 1.08 AS taxed FROM orders WHERE amount * 1.08 > 100;\n\n-- ✅ Or use a CTE to compute once, filter twice (cleaner for complex expressions)\nWITH taxed_orders AS (\n  SELECT *, amount * 1.08 AS taxed FROM orders\n)\nSELECT * FROM taxed_orders WHERE taxed > 100;\n",[22,2087,2088,2093,2125,2129,2134,2175,2179,2184,2196,2221,2226],{"__ignoreMap":35},[39,2089,2090],{"class":41,"line":42},[39,2091,2092],{"class":45},"-- ❌ ERROR: alias \"taxed\" doesn't exist in WHERE (WHERE runs before SELECT)\n",[39,2094,2095,2097,2099,2101,2103,2105,2107,2109,2111,2113,2115,2117,2119,2121,2123],{"class":41,"line":49},[39,2096,24],{"class":64},[39,2098,411],{"class":67},[39,2100,414],{"class":64},[39,2102,417],{"class":208},[39,2104,420],{"class":67},[39,2106,423],{"class":208},[39,2108,257],{"class":64},[39,2110,520],{"class":67},[39,2112,74],{"class":64},[39,2114,975],{"class":67},[39,2116,83],{"class":64},[39,2118,520],{"class":67},[39,2120,777],{"class":64},[39,2122,984],{"class":208},[39,2124,942],{"class":67},[39,2126,2127],{"class":41,"line":55},[39,2128,159],{"emptyLinePlaceholder":158},[39,2130,2131],{"class":41,"line":61},[39,2132,2133],{"class":45},"-- ✅ Repeat the expression\n",[39,2135,2136,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158,2160,2162,2164,2166,2168,2171,2173],{"class":41,"line":71},[39,2137,24],{"class":64},[39,2139,411],{"class":67},[39,2141,414],{"class":64},[39,2143,417],{"class":208},[39,2145,420],{"class":67},[39,2147,423],{"class":208},[39,2149,257],{"class":64},[39,2151,520],{"class":67},[39,2153,74],{"class":64},[39,2155,975],{"class":67},[39,2157,83],{"class":64},[39,2159,411],{"class":67},[39,2161,414],{"class":64},[39,2163,417],{"class":208},[39,2165,420],{"class":67},[39,2167,423],{"class":208},[39,2169,2170],{"class":64}," >",[39,2172,984],{"class":208},[39,2174,942],{"class":67},[39,2176,2177],{"class":41,"line":80},[39,2178,159],{"emptyLinePlaceholder":158},[39,2180,2181],{"class":41,"line":172},[39,2182,2183],{"class":45},"-- ✅ Or use a CTE to compute once, filter twice (cleaner for complex expressions)\n",[39,2185,2186,2189,2192,2194],{"class":41,"line":181},[39,2187,2188],{"class":64},"WITH",[39,2190,2191],{"class":67}," taxed_orders ",[39,2193,196],{"class":64},[39,2195,1670],{"class":67},[39,2197,2198,2200,2202,2205,2207,2209,2211,2213,2215,2217,2219],{"class":41,"line":205},[39,2199,1675],{"class":64},[39,2201,317],{"class":64},[39,2203,2204],{"class":67},", amount ",[39,2206,414],{"class":64},[39,2208,417],{"class":208},[39,2210,420],{"class":67},[39,2212,423],{"class":208},[39,2214,257],{"class":64},[39,2216,520],{"class":67},[39,2218,74],{"class":64},[39,2220,507],{"class":67},[39,2222,2223],{"class":41,"line":220},[39,2224,2225],{"class":67},")\n",[39,2227,2228,2230,2232,2234,2236,2238,2240,2242,2244],{"class":41,"line":227},[39,2229,24],{"class":64},[39,2231,317],{"class":64},[39,2233,320],{"class":64},[39,2235,2191],{"class":67},[39,2237,83],{"class":64},[39,2239,520],{"class":67},[39,2241,777],{"class":64},[39,2243,984],{"class":208},[39,2245,942],{"class":67},[14,2247,2249],{"id":2248},"complex-implementation-multi-condition-report-filter","Complex Implementation: Multi-Condition Report Filter",[27,2251,2252],{"language":29},[31,2253,2255],{"className":33,"code":2254,"language":29,"meta":35,"style":35},"-- A realistic report query: find high-value customers in major cities\n-- who haven't placed any orders over $500 in the last 90 days.\nSELECT\n  c.id,\n  c.name,\n  c.city,\n  c.created_at\nFROM customers c\nWHERE c.city IN ('NYC', 'LA', 'SF')                         -- restrict to major cities (uses index)\n  AND c.name LIKE 'A%'                                       -- name filter (index-accelerated if no leading %)\n  AND c.deleted_at IS NULL                                    -- exclude soft-deleted\n  AND NOT EXISTS (                                            -- anti-join: no large recent orders\n    SELECT 1 FROM orders o\n    WHERE o.customer_id = c.id\n      AND o.amount > 500\n      AND o.ordered_on >= CURRENT_DATE - INTERVAL '90 days'\n  )\nORDER BY c.created_at DESC;\n",[22,2256,2257,2262,2267,2271,2282,2292,2303,2312,2318,2352,2370,2389,2403,2415,2434,2451,2477,2482],{"__ignoreMap":35},[39,2258,2259],{"class":41,"line":42},[39,2260,2261],{"class":45},"-- A realistic report query: find high-value customers in major cities\n",[39,2263,2264],{"class":41,"line":49},[39,2265,2266],{"class":45},"-- who haven't placed any orders over $500 in the last 90 days.\n",[39,2268,2269],{"class":41,"line":55},[39,2270,169],{"class":64},[39,2272,2273,2276,2278,2280],{"class":41,"line":61},[39,2274,2275],{"class":208},"  c",[39,2277,420],{"class":67},[39,2279,1703],{"class":208},[39,2281,178],{"class":67},[39,2283,2284,2286,2288,2290],{"class":41,"line":71},[39,2285,2275],{"class":208},[39,2287,420],{"class":67},[39,2289,190],{"class":208},[39,2291,178],{"class":67},[39,2293,2294,2296,2298,2301],{"class":41,"line":80},[39,2295,2275],{"class":208},[39,2297,420],{"class":67},[39,2299,2300],{"class":208},"city",[39,2302,178],{"class":67},[39,2304,2305,2307,2309],{"class":41,"line":172},[39,2306,2275],{"class":208},[39,2308,420],{"class":67},[39,2310,2311],{"class":208},"created_at\n",[39,2313,2314,2316],{"class":41,"line":181},[39,2315,74],{"class":64},[39,2317,1661],{"class":67},[39,2319,2320,2322,2324,2326,2328,2331,2333,2336,2338,2341,2343,2346,2349],{"class":41,"line":205},[39,2321,83],{"class":64},[39,2323,1698],{"class":208},[39,2325,420],{"class":67},[39,2327,2300],{"class":208},[39,2329,2330],{"class":64}," IN",[39,2332,1037],{"class":67},[39,2334,2335],{"class":253},"'NYC'",[39,2337,378],{"class":67},[39,2339,2340],{"class":253},"'LA'",[39,2342,378],{"class":67},[39,2344,2345],{"class":253},"'SF'",[39,2347,2348],{"class":67},")                         ",[39,2350,2351],{"class":45},"-- restrict to major cities (uses index)\n",[39,2353,2354,2357,2359,2361,2363,2365,2367],{"class":41,"line":220},[39,2355,2356],{"class":64},"  AND",[39,2358,1698],{"class":208},[39,2360,420],{"class":67},[39,2362,190],{"class":208},[39,2364,1065],{"class":64},[39,2366,1068],{"class":253},[39,2368,2369],{"class":45},"                                       -- name filter (index-accelerated if no leading %)\n",[39,2371,2372,2374,2376,2378,2381,2384,2386],{"class":41,"line":227},[39,2373,2356],{"class":64},[39,2375,1698],{"class":208},[39,2377,420],{"class":67},[39,2379,2380],{"class":208},"deleted_at",[39,2382,2383],{"class":64}," IS",[39,2385,1118],{"class":64},[39,2387,2388],{"class":45},"                                    -- exclude soft-deleted\n",[39,2390,2391,2393,2395,2397,2400],{"class":41,"line":232},[39,2392,2356],{"class":64},[39,2394,1534],{"class":64},[39,2396,1537],{"class":64},[39,2398,2399],{"class":67}," (                                            ",[39,2401,2402],{"class":45},"-- anti-join: no large recent orders\n",[39,2404,2405,2408,2410,2412],{"class":41,"line":238},[39,2406,2407],{"class":64},"    SELECT",[39,2409,417],{"class":208},[39,2411,320],{"class":64},[39,2413,2414],{"class":67}," orders o\n",[39,2416,2417,2420,2422,2424,2426,2428,2430,2432],{"class":41,"line":243},[39,2418,2419],{"class":64},"    WHERE",[39,2421,1687],{"class":208},[39,2423,420],{"class":67},[39,2425,1692],{"class":208},[39,2427,1695],{"class":64},[39,2429,1698],{"class":208},[39,2431,420],{"class":67},[39,2433,1766],{"class":208},[39,2435,2436,2439,2441,2443,2446,2448],{"class":41,"line":250},[39,2437,2438],{"class":64},"      AND",[39,2440,1687],{"class":208},[39,2442,420],{"class":67},[39,2444,2445],{"class":208},"amount",[39,2447,2170],{"class":64},[39,2449,2450],{"class":208}," 500\n",[39,2452,2453,2455,2457,2459,2462,2465,2468,2471,2474],{"class":41,"line":266},[39,2454,2438],{"class":64},[39,2456,1687],{"class":208},[39,2458,420],{"class":67},[39,2460,2461],{"class":208},"ordered_on",[39,2463,2464],{"class":64}," >=",[39,2466,2467],{"class":67}," CURRENT_DATE ",[39,2469,2470],{"class":64},"-",[39,2472,2473],{"class":67}," INTERVAL ",[39,2475,2476],{"class":253},"'90 days'\n",[39,2478,2479],{"class":41,"line":286},[39,2480,2481],{"class":67},"  )\n",[39,2483,2485,2487,2489,2491,2494,2497],{"class":41,"line":2484},18,[39,2486,517],{"class":64},[39,2488,1698],{"class":208},[39,2490,420],{"class":67},[39,2492,2493],{"class":208},"created_at",[39,2495,2496],{"class":64}," DESC",[39,2498,942],{"class":67},[14,2500,2502,2503,2505],{"id":2501},"anti-pattern-select-in-production","Anti-Pattern: ",[22,2504,297],{}," in Production",[27,2507,2508],{"language":29},[31,2509,2511],{"className":33,"code":2510,"language":29,"meta":35,"style":35},"-- ❌ WRONG: SELECT * in a production query\n-- Problems: fetches all columns (wasteful), breaks if schema changes,\n-- prevents index-only scans, exposes sensitive columns (e.g., password_hash)\nSELECT * FROM customers WHERE city = 'NYC';\n\n-- ✅ RIGHT: explicit columns\nSELECT id, name, city, created_at FROM customers WHERE city = 'NYC';\n",[22,2512,2513,2518,2523,2528,2548,2552,2557],{"__ignoreMap":35},[39,2514,2515],{"class":41,"line":42},[39,2516,2517],{"class":45},"-- ❌ WRONG: SELECT * in a production query\n",[39,2519,2520],{"class":41,"line":49},[39,2521,2522],{"class":45},"-- Problems: fetches all columns (wasteful), breaks if schema changes,\n",[39,2524,2525],{"class":41,"line":55},[39,2526,2527],{"class":45},"-- prevents index-only scans, exposes sensitive columns (e.g., password_hash)\n",[39,2529,2530,2532,2534,2536,2538,2540,2542,2544,2546],{"class":41,"line":61},[39,2531,24],{"class":64},[39,2533,317],{"class":64},[39,2535,320],{"class":64},[39,2537,930],{"class":67},[39,2539,83],{"class":64},[39,2541,569],{"class":67},[39,2543,731],{"class":64},[39,2545,939],{"class":253},[39,2547,942],{"class":67},[39,2549,2550],{"class":41,"line":71},[39,2551,159],{"emptyLinePlaceholder":158},[39,2553,2554],{"class":41,"line":80},[39,2555,2556],{"class":45},"-- ✅ RIGHT: explicit columns\n",[39,2558,2559,2561,2563,2565,2567,2569,2571,2573,2575,2577,2579],{"class":41,"line":172},[39,2560,24],{"class":64},[39,2562,338],{"class":67},[39,2564,190],{"class":64},[39,2566,343],{"class":67},[39,2568,74],{"class":64},[39,2570,930],{"class":67},[39,2572,83],{"class":64},[39,2574,569],{"class":67},[39,2576,731],{"class":64},[39,2578,939],{"class":253},[39,2580,942],{"class":67},[14,2582,2584],{"id":2583},"tips-tricks","💡 Tips & Tricks",[88,2586,2587],{},[91,2588,2589,2592,2593,2596,2597,2600,2601,2603],{},[97,2590,2591],{},"Idiom",": use ",[22,2594,2595],{},"IN (...)"," for a fixed list of values, but switch to ",[22,2598,2599],{},"= ANY(ARRAY[...])"," in PostgreSQL when the list is large or comes from a parameter — ",[22,2602,1034],{}," with thousands of values can hit parser limits and is slower to parse.",[27,2605,2606],{"language":29},[31,2607,2609],{"className":33,"code":2608,"language":29,"meta":35,"style":35},"-- Large IN list — can be slow to parse and hit limits\nSELECT * FROM orders WHERE customer_id IN (1, 2, 3, \u002F* ... 5000 values *\u002F);\n\n-- Better for large lists — ARRAY is passed as a single parameter, parsed once\nSELECT * FROM orders WHERE customer_id = ANY(ARRAY[1, 2, 3, \u002F* ... 5000 values *\u002F]::bigint[]);\n",[22,2610,2611,2616,2652,2656,2661],{"__ignoreMap":35},[39,2612,2613],{"class":41,"line":42},[39,2614,2615],{"class":45},"-- Large IN list — can be slow to parse and hit limits\n",[39,2617,2618,2620,2622,2624,2626,2628,2630,2632,2634,2636,2638,2641,2643,2645,2647,2650],{"class":41,"line":49},[39,2619,24],{"class":64},[39,2621,317],{"class":64},[39,2623,320],{"class":64},[39,2625,975],{"class":67},[39,2627,83],{"class":64},[39,2629,1031],{"class":67},[39,2631,1034],{"class":64},[39,2633,1037],{"class":67},[39,2635,1040],{"class":208},[39,2637,378],{"class":67},[39,2639,2640],{"class":208},"2",[39,2642,378],{"class":67},[39,2644,1045],{"class":208},[39,2646,378],{"class":67},[39,2648,2649],{"class":45},"\u002F* ... 5000 values *\u002F",[39,2651,1048],{"class":67},[39,2653,2654],{"class":41,"line":55},[39,2655,159],{"emptyLinePlaceholder":158},[39,2657,2658],{"class":41,"line":61},[39,2659,2660],{"class":45},"-- Better for large lists — ARRAY is passed as a single parameter, parsed once\n",[39,2662,2663,2665,2667,2669,2671,2673,2675,2677,2680,2683,2686,2689],{"class":41,"line":71},[39,2664,24],{"class":64},[39,2666,317],{"class":64},[39,2668,320],{"class":64},[39,2670,975],{"class":67},[39,2672,83],{"class":64},[39,2674,1031],{"class":67},[39,2676,731],{"class":64},[39,2678,2679],{"class":67}," ANY(",[39,2681,2682],{"class":64},"ARRAY",[39,2684,2685],{"class":67},"[1, 2, 3, \u002F* ... 5000 values *\u002F]::",[39,2687,2688],{"class":64},"bigint",[39,2690,2691],{"class":67},"[]);\n",[88,2693,2694,2714,2732,2754],{},[91,2695,2696,2699,2700,2703,2704,2707,2708,2710,2711,2713],{},[97,2697,2698],{},"Performance",": ",[22,2701,2702],{},"EXISTS (SELECT 1 FROM ... WHERE ...)"," is usually faster than ",[22,2705,2706],{},"IN (SELECT ...)"," for subqueries over large sets, because ",[22,2709,1775],{}," short-circuits on the first match while ",[22,2712,1034],{}," may materialize the full list. The difference is most visible when the subquery can produce NULLs.",[91,2715,2716,2719,2720,2722,2723,2725,2726,2728,2729,2731],{},[97,2717,2718],{},"Portability",": prefer ",[22,2721,750],{}," over ",[22,2724,753],{}," — ",[22,2727,750],{}," is ANSI standard and works everywhere; ",[22,2730,753],{}," is supported by most engines but not all (notably MS Access and some older engines reject it).",[91,2733,2734,2737,2738,2741,2742,2745,2746,2749,2750,2753],{},[97,2735,2736],{},"Debug",": when a query returns fewer rows than expected, check for ",[22,2739,2740],{},"NOT IN"," with a NULL-producing subquery — ",[22,2743,2744],{},"x NOT IN (1, 2, NULL)"," returns ",[537,2747,2748],{},"zero"," rows for the entire query (NULL poison). Use ",[22,2751,2752],{},"NOT EXISTS"," instead.",[91,2755,2756,2592,2758,2761,2762,2764,2765,2767,2768,2771],{},[97,2757,2591],{},[22,2759,2760],{},"COALESCE(column, fallback)"," to substitute a default for NULL in the ",[22,2763,24],{}," list, but don't use it in ",[22,2766,83],{}," to \"fix\" NULL filtering — it defeats indexes (the function is applied per row after index lookup is decided). Add a partial index or use ",[22,2769,2770],{},"OR column IS NULL"," explicitly.",[14,2773,2775],{"id":2774},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[88,2777,2778,2802,2826],{},[91,2779,2780,2785,2786,2788,2789,2792,2793,2795,2796,2798,2799,2801],{},[97,2781,2782,2784],{},[22,2783,731],{}," is single-equals",": SQL uses ",[22,2787,731],{}," for both assignment (in ",[22,2790,2791],{},"SET",") and comparison (in ",[22,2794,83],{},"). There is no ",[22,2797,743],{},". Programmers coming from JS\u002FPython\u002FC often type ",[22,2800,743],{}," and get a syntax error.",[91,2803,2804,2699,2810,2813,2814,2817,2818,2821,2822,2825],{},[97,2805,2806,2809],{},[22,2807,2808],{},"!= NULL"," is never true",[22,2811,2812],{},"WHERE col != NULL"," returns zero rows — use ",[22,2815,2816],{},"WHERE col IS NOT NULL",". Even ",[22,2819,2820],{},"WHERE col = NULL"," returns zero rows. NULL comparisons are ",[537,2823,2824],{},"always"," UNKNOWN.",[91,2827,2828,2699,2833,2745,2836,2839,2840,2843,2844,2847,2848,2851,2852,2854,2855,2857,2858,2860,2861,2864],{},[97,2829,2830,2832],{},[22,2831,2740],{}," with NULLs (NULL poison)",[22,2834,2835],{},"WHERE x NOT IN (1, 2, NULL)",[537,2837,2838],{},"no rows at all"," — because ",[22,2841,2842],{},"x NOT IN (a, b, c)"," is equivalent to ",[22,2845,2846],{},"x \u003C> a AND x \u003C> b AND x \u003C> c",", and ",[22,2849,2850],{},"x \u003C> NULL"," is UNKNOWN, which makes the whole ",[22,2853,1176],{}," chain UNKNOWN, which ",[22,2856,83],{}," treats as \"not true.\" This is the most dangerous NULL gotcha in SQL. Use ",[22,2859,2752],{}," or add ",[22,2862,2863],{},"AND col IS NOT NULL"," to the subquery.",[27,2866,2867],{"language":29},[31,2868,2870],{"className":33,"code":2869,"language":29,"meta":35,"style":35},"-- ❌ NULL poison: if the subquery returns any NULL, the entire NOT IN returns zero rows\nSELECT * FROM customers WHERE id NOT IN (SELECT customer_id FROM orders WHERE city IS NULL);\n\n-- ✅ Safe: NOT EXISTS is immune to NULLs in the subquery\nSELECT * FROM customers c\nWHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.city IS NULL);\n",[22,2871,2872,2877,2915,2919,2924,2934],{"__ignoreMap":35},[39,2873,2874],{"class":41,"line":42},[39,2875,2876],{"class":45},"-- ❌ NULL poison: if the subquery returns any NULL, the entire NOT IN returns zero rows\n",[39,2878,2879,2881,2883,2885,2887,2889,2891,2893,2895,2897,2899,2901,2903,2905,2907,2909,2911,2913],{"class":41,"line":49},[39,2880,24],{"class":64},[39,2882,317],{"class":64},[39,2884,320],{"class":64},[39,2886,930],{"class":67},[39,2888,83],{"class":64},[39,2890,1628],{"class":67},[39,2892,1196],{"class":64},[39,2894,2330],{"class":64},[39,2896,1037],{"class":67},[39,2898,24],{"class":64},[39,2900,1031],{"class":67},[39,2902,74],{"class":64},[39,2904,975],{"class":67},[39,2906,83],{"class":64},[39,2908,569],{"class":67},[39,2910,1115],{"class":64},[39,2912,1118],{"class":64},[39,2914,1048],{"class":67},[39,2916,2917],{"class":41,"line":55},[39,2918,159],{"emptyLinePlaceholder":158},[39,2920,2921],{"class":41,"line":61},[39,2922,2923],{"class":45},"-- ✅ Safe: NOT EXISTS is immune to NULLs in the subquery\n",[39,2925,2926,2928,2930,2932],{"class":41,"line":71},[39,2927,24],{"class":64},[39,2929,317],{"class":64},[39,2931,320],{"class":64},[39,2933,1661],{"class":67},[39,2935,2936,2938,2940,2942,2944,2946,2948,2950,2952,2954,2956,2958,2960,2962,2964,2966,2968,2970,2972,2974,2976,2978,2980],{"class":41,"line":80},[39,2937,83],{"class":64},[39,2939,1534],{"class":64},[39,2941,1537],{"class":64},[39,2943,1037],{"class":67},[39,2945,24],{"class":64},[39,2947,417],{"class":208},[39,2949,320],{"class":64},[39,2951,1682],{"class":67},[39,2953,83],{"class":64},[39,2955,1687],{"class":208},[39,2957,420],{"class":67},[39,2959,1692],{"class":208},[39,2961,1695],{"class":64},[39,2963,1698],{"class":208},[39,2965,420],{"class":67},[39,2967,1703],{"class":208},[39,2969,1009],{"class":64},[39,2971,1687],{"class":208},[39,2973,420],{"class":67},[39,2975,2300],{"class":208},[39,2977,2383],{"class":64},[39,2979,1118],{"class":64},[39,2981,1048],{"class":67},[88,2983,2984,3007,3023,3041,3052],{},[91,2985,2986,2699,2989,2992,2993,2996,2997,3000,3001,693,3003,3006],{},[97,2987,2988],{},"Case-sensitive string comparison in PostgreSQL",[22,2990,2991],{},"WHERE name = 'alice'"," does ",[537,2994,2995],{},"not"," match ",[22,2998,2999],{},"'Alice'",". Use ",[22,3002,869],{},[22,3004,3005],{},"LOWER(name) = LOWER('alice')"," (or a case-insensitive collation). MySQL's default collation is case-insensitive — a portability trap.",[91,3008,3009,2699,3012,1211,3015,3018,3019,3022],{},[97,3010,3011],{},"Trailing whitespace",[22,3013,3014],{},"'alice' = 'alice '",[22,3016,3017],{},"false"," in PostgreSQL (strings compared literally, including trailing spaces), but ",[22,3020,3021],{},"true"," in some other engines. Don't rely on implicit trimming.",[91,3024,3025,2699,3030,3033,3034,740,3037,3040],{},[97,3026,3027,3029],{},[22,3028,1003],{}," is inclusive on both ends",[22,3031,3032],{},"amount BETWEEN 10 AND 100"," means ",[22,3035,3036],{},">= 10 AND \u003C= 100",[22,3038,3039],{},"> 10 AND \u003C 100",". Off-by-one errors here are common.",[91,3042,3043,2699,3048,3051],{},[97,3044,3045,3047],{},[22,3046,1034],{}," with mixed types",[22,3049,3050],{},"WHERE id IN ('1', '2', '3')"," against an integer column triggers implicit casts — can mask bugs and prevent index use. Match the column type.",[91,3053,3054,3059,3060,3062,3063,3066,3067,3070,3071,3073],{},[97,3055,3056,3057],{},"Column order in ",[22,3058,297],{},": the order of columns in ",[22,3061,297],{}," is the order they were defined in ",[22,3064,3065],{},"CREATE TABLE"," (or the order in ",[22,3068,3069],{},"ALTER TABLE ADD COLUMN","). It's not guaranteed stable across schema changes — another reason to avoid ",[22,3072,414],{}," in application code.",[14,3075,3077],{"id":3076},"spot-the-bug","🧠 Spot the Bug",[19,3079,3080,3081,3083,3084,3086],{},"This query is supposed to find customers who are ",[537,3082,2995],{}," in NYC, but it's missing Carol, whose ",[22,3085,2300],{}," is NULL. Why, and how do you fix it?",[27,3088,3089],{"language":29},[31,3090,3092],{"className":33,"code":3091,"language":29,"meta":35,"style":35},"SELECT name FROM customers WHERE city != 'NYC';\n",[22,3093,3094],{"__ignoreMap":35},[39,3095,3096,3098,3100,3102,3104,3106,3108,3110,3112],{"class":41,"line":42},[39,3097,24],{"class":64},[39,3099,145],{"class":64},[39,3101,320],{"class":64},[39,3103,930],{"class":67},[39,3105,83],{"class":64},[39,3107,569],{"class":67},[39,3109,753],{"class":64},[39,3111,939],{"class":253},[39,3113,942],{"class":67},[3115,3116,3117,3121,3139,3142,3245,3261],"details",{},[3118,3119,3120],"summary",{},"Answer",[19,3122,3123,1211,3126,3128,3129,1211,3131,3133,3134,1967,3136,3138],{},[22,3124,3125],{},"city != 'NYC'",[22,3127,696],{}," when ",[22,3130,2300],{},[22,3132,1788],{}," (any comparison with NULL yields UNKNOWN, not TRUE or FALSE). ",[22,3135,83],{},[22,3137,688],{},", so NULL-city rows are excluded — even though \"unknown city\" intuitively means \"not NYC.\"",[19,3140,3141],{},"The fix depends on intent:",[27,3143,3144],{"language":29},[31,3145,3147],{"className":33,"code":3146,"language":29,"meta":35,"style":35},"-- If \"not NYC\" includes unknown cities:\nSELECT name FROM customers WHERE city IS DISTINCT FROM 'NYC';\n-- or\nSELECT name FROM customers WHERE city != 'NYC' OR city IS NULL;\n\n-- If \"not NYC\" means \"known to be not NYC\":\nSELECT name FROM customers WHERE city != 'NYC';\n-- (NULLs correctly excluded — this is the rare case where the original is right)\n",[22,3148,3149,3154,3178,3183,3211,3215,3220,3240],{"__ignoreMap":35},[39,3150,3151],{"class":41,"line":42},[39,3152,3153],{"class":45},"-- If \"not NYC\" includes unknown cities:\n",[39,3155,3156,3158,3160,3162,3164,3166,3168,3170,3172,3174,3176],{"class":41,"line":49},[39,3157,24],{"class":64},[39,3159,145],{"class":64},[39,3161,320],{"class":64},[39,3163,930],{"class":67},[39,3165,83],{"class":64},[39,3167,569],{"class":67},[39,3169,1115],{"class":64},[39,3171,1143],{"class":64},[39,3173,320],{"class":64},[39,3175,939],{"class":253},[39,3177,942],{"class":67},[39,3179,3180],{"class":41,"line":55},[39,3181,3182],{"class":45},"-- or\n",[39,3184,3185,3187,3189,3191,3193,3195,3197,3199,3201,3203,3205,3207,3209],{"class":41,"line":61},[39,3186,24],{"class":64},[39,3188,145],{"class":64},[39,3190,320],{"class":64},[39,3192,930],{"class":67},[39,3194,83],{"class":64},[39,3196,569],{"class":67},[39,3198,753],{"class":64},[39,3200,939],{"class":253},[39,3202,1250],{"class":64},[39,3204,569],{"class":67},[39,3206,1115],{"class":64},[39,3208,1118],{"class":64},[39,3210,942],{"class":67},[39,3212,3213],{"class":41,"line":71},[39,3214,159],{"emptyLinePlaceholder":158},[39,3216,3217],{"class":41,"line":80},[39,3218,3219],{"class":45},"-- If \"not NYC\" means \"known to be not NYC\":\n",[39,3221,3222,3224,3226,3228,3230,3232,3234,3236,3238],{"class":41,"line":172},[39,3223,24],{"class":64},[39,3225,145],{"class":64},[39,3227,320],{"class":64},[39,3229,930],{"class":67},[39,3231,83],{"class":64},[39,3233,569],{"class":67},[39,3235,753],{"class":64},[39,3237,939],{"class":253},[39,3239,942],{"class":67},[39,3241,3242],{"class":41,"line":181},[39,3243,3244],{"class":45},"-- (NULLs correctly excluded — this is the rare case where the original is right)\n",[19,3246,3247,3249,3250,3252,3253,3255,3256,3252,3258,3260],{},[22,3248,2063],{}," is the NULL-safe \"not equal\" — it treats ",[22,3251,1788],{}," and ",[22,3254,1788],{}," as equal (distinct = false), and ",[22,3257,1788],{},[22,3259,2335],{}," as distinct (true). It's the cleanest way to say \"not equal, counting NULL as a real value.\"",[19,3262,3263,2699,3266,3252,3268,3270,3271,3273,3274,420],{},[97,3264,3265],{},"The lesson",[22,3267,753],{},[22,3269,750],{}," silently drop NULLs. If NULL is a meaningful \"other\" value in your data, use ",[22,3272,2063],{}," or explicitly handle ",[22,3275,883],{},[14,3277,3278],{"id":3118},"Summary",[19,3280,3281,3282,3284],{},"You can now project columns, alias them, remove duplicates, and filter rows with ",[22,3283,83],{}," using comparison and logical operators — while avoiding the NULL traps that catch most beginners. Next: sorting results and paginating through large result sets.",[3286,3287,3288],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}",{"title":35,"searchDepth":49,"depth":49,"links":3290},[3291,3292,3296,3297,3301,3305,3306,3307,3308,3309,3310,3311,3313,3314,3315,3316],{"id":16,"depth":49,"text":17},{"id":120,"depth":49,"text":121,"children":3293},[3294],{"id":294,"depth":55,"text":3295},"SELECT * — the production anti-pattern",{"id":391,"depth":49,"text":392},{"id":548,"depth":49,"text":549,"children":3298},[3299],{"id":611,"depth":55,"text":3300},"DISTINCT ON (PostgreSQL-specific)",{"id":679,"depth":49,"text":680,"children":3302},[3303,3304],{"id":700,"depth":55,"text":701},{"id":1156,"depth":55,"text":1157},{"id":1367,"depth":49,"text":1368},{"id":1597,"depth":49,"text":1598},{"id":1782,"depth":49,"text":1783},{"id":1976,"depth":49,"text":1977},{"id":2070,"depth":49,"text":2071},{"id":2248,"depth":49,"text":2249},{"id":2501,"depth":49,"text":3312},"Anti-Pattern: SELECT * in Production",{"id":2583,"depth":49,"text":2584},{"id":2774,"depth":49,"text":2775},{"id":3076,"depth":49,"text":3077},{"id":3118,"depth":49,"text":3278},"md",{},"\u002Fsql\u002F02-select-basics-and-filtering",{"title":5,"description":35},"sql\u002F02-select-basics-and-filtering","t-jw0-wNZXL5Uox09ICFJ2w__PlghZ72JlnXYSKFTE0",1789924654241]