[{"data":1,"prerenderedAt":1460},["ShallowReactive",2],{"blog-\u002Fblog\u002Fsql-case-coalesce-null-handling":3},{"id":4,"title":5,"body":6,"description":1446,"difficulty":1447,"extension":1448,"framework":1449,"frameworkSlug":41,"meta":1450,"navigation":282,"order":92,"path":1451,"qaPath":1452,"seo":1453,"stem":1454,"subtopic":1455,"topic":1456,"topicSlug":1457,"updated":1458,"__hash__":1459},"blog\u002Fblog\u002Fsql-case-coalesce-null-handling.md","SQL CASE, COALESCE, and NULL Handling — Practical Patterns",{"type":7,"value":8,"toc":1434},"minimark",[9,14,36,135,139,155,391,395,506,510,516,676,680,686,822,826,832,1042,1049,1085,1089,1221,1225,1236,1288,1292,1388,1392,1430],[10,11,13],"h2",{"id":12},"null-is-not-zero-and-not-empty-string","NULL is not zero and not empty string",[15,16,17,21,22,26,27,29,30,32,33,35],"p",{},[18,19,20],"code",{},"NULL"," in SQL means ",[23,24,25],"em",{},"unknown",". It is not zero, it is not an empty string, and\nit does not equal anything — not even another ",[18,28,20],{},". Any arithmetic or\ncomparison involving ",[18,31,20],{}," returns ",[18,34,20],{}," (or UNKNOWN for boolean expressions).\nEvery SQL developer runs into this eventually; understanding it up front saves\nhours of debugging.",[37,38,43],"pre",{"className":39,"code":40,"language":41,"meta":42,"style":42},"language-sql shiki shiki-themes github-light github-dark","SELECT NULL = NULL;    -- UNKNOWN (not TRUE)\nSELECT NULL + 100;     -- NULL\nSELECT NULL || 'hello'; -- NULL (Postgres) or 'hello' (MySQL CONCAT)\nSELECT COALESCE(NULL, 'fallback');  -- 'fallback'\n","sql","",[18,44,45,70,90,110],{"__ignoreMap":42},[46,47,50,54,57,60,62,66],"span",{"class":48,"line":49},"line",1,[46,51,53],{"class":52},"szBVR","SELECT",[46,55,56],{"class":52}," NULL",[46,58,59],{"class":52}," =",[46,61,56],{"class":52},[46,63,65],{"class":64},"sVt8B",";    ",[46,67,69],{"class":68},"sJ8bj","-- UNKNOWN (not TRUE)\n",[46,71,73,75,77,80,84,87],{"class":48,"line":72},2,[46,74,53],{"class":52},[46,76,56],{"class":52},[46,78,79],{"class":52}," +",[46,81,83],{"class":82},"sj4cs"," 100",[46,85,86],{"class":64},";     ",[46,88,89],{"class":68},"-- NULL\n",[46,91,93,95,97,100,104,107],{"class":48,"line":92},3,[46,94,53],{"class":52},[46,96,56],{"class":52},[46,98,99],{"class":52}," ||",[46,101,103],{"class":102},"sZZnC"," 'hello'",[46,105,106],{"class":64},"; ",[46,108,109],{"class":68},"-- NULL (Postgres) or 'hello' (MySQL CONCAT)\n",[46,111,113,115,118,121,123,126,129,132],{"class":48,"line":112},4,[46,114,53],{"class":52},[46,116,117],{"class":82}," COALESCE",[46,119,120],{"class":64},"(",[46,122,20],{"class":52},[46,124,125],{"class":64},", ",[46,127,128],{"class":102},"'fallback'",[46,130,131],{"class":64},");  ",[46,133,134],{"class":68},"-- 'fallback'\n",[10,136,138],{"id":137},"case-sqls-ifelse","CASE — SQL's if\u002Felse",[15,140,141,144,145,125,147,150,151,154],{},[18,142,143],{},"CASE"," returns a value based on a condition. It appears in ",[18,146,53],{},[18,148,149],{},"WHERE",",\n",[18,152,153],{},"ORDER BY",", and inside aggregate functions.",[37,156,158],{"className":39,"code":157,"language":41,"meta":42,"style":42},"-- Label orders by size tier\nSELECT\n    id,\n    customer_id,\n    total_amount,\n    CASE\n        WHEN total_amount >= 500 THEN 'Large'\n        WHEN total_amount >= 100 THEN 'Medium'\n        WHEN total_amount >    0 THEN 'Small'\n        ELSE 'Free'\n    END AS order_tier\nFROM orders;\n\n-- Map a status code to a human-readable label (simple CASE form)\nSELECT\n    id,\n    CASE status\n        WHEN 'pending'   THEN 'Awaiting payment'\n        WHEN 'shipped'   THEN 'On its way'\n        WHEN 'delivered' THEN 'Delivered'\n        WHEN 'cancelled' THEN 'Cancelled'\n        ELSE 'Unknown status: ' || status\n    END AS status_label\nFROM orders;\n",[18,159,160,165,170,175,180,186,192,213,229,247,256,268,277,284,290,295,300,309,323,336,349,362,374,384],{"__ignoreMap":42},[46,161,162],{"class":48,"line":49},[46,163,164],{"class":68},"-- Label orders by size tier\n",[46,166,167],{"class":48,"line":72},[46,168,169],{"class":52},"SELECT\n",[46,171,172],{"class":48,"line":92},[46,173,174],{"class":64},"    id,\n",[46,176,177],{"class":48,"line":112},[46,178,179],{"class":64},"    customer_id,\n",[46,181,183],{"class":48,"line":182},5,[46,184,185],{"class":64},"    total_amount,\n",[46,187,189],{"class":48,"line":188},6,[46,190,191],{"class":52},"    CASE\n",[46,193,195,198,201,204,207,210],{"class":48,"line":194},7,[46,196,197],{"class":52},"        WHEN",[46,199,200],{"class":64}," total_amount ",[46,202,203],{"class":52},">=",[46,205,206],{"class":82}," 500",[46,208,209],{"class":52}," THEN",[46,211,212],{"class":102}," 'Large'\n",[46,214,216,218,220,222,224,226],{"class":48,"line":215},8,[46,217,197],{"class":52},[46,219,200],{"class":64},[46,221,203],{"class":52},[46,223,83],{"class":82},[46,225,209],{"class":52},[46,227,228],{"class":102}," 'Medium'\n",[46,230,232,234,236,239,242,244],{"class":48,"line":231},9,[46,233,197],{"class":52},[46,235,200],{"class":64},[46,237,238],{"class":52},">",[46,240,241],{"class":82},"    0",[46,243,209],{"class":52},[46,245,246],{"class":102}," 'Small'\n",[46,248,250,253],{"class":48,"line":249},10,[46,251,252],{"class":52},"        ELSE",[46,254,255],{"class":102}," 'Free'\n",[46,257,259,262,265],{"class":48,"line":258},11,[46,260,261],{"class":52},"    END",[46,263,264],{"class":52}," AS",[46,266,267],{"class":64}," order_tier\n",[46,269,271,274],{"class":48,"line":270},12,[46,272,273],{"class":52},"FROM",[46,275,276],{"class":64}," orders;\n",[46,278,280],{"class":48,"line":279},13,[46,281,283],{"emptyLinePlaceholder":282},true,"\n",[46,285,287],{"class":48,"line":286},14,[46,288,289],{"class":68},"-- Map a status code to a human-readable label (simple CASE form)\n",[46,291,293],{"class":48,"line":292},15,[46,294,169],{"class":52},[46,296,298],{"class":48,"line":297},16,[46,299,174],{"class":64},[46,301,303,306],{"class":48,"line":302},17,[46,304,305],{"class":52},"    CASE",[46,307,308],{"class":52}," status\n",[46,310,312,314,317,320],{"class":48,"line":311},18,[46,313,197],{"class":52},[46,315,316],{"class":102}," 'pending'",[46,318,319],{"class":52},"   THEN",[46,321,322],{"class":102}," 'Awaiting payment'\n",[46,324,326,328,331,333],{"class":48,"line":325},19,[46,327,197],{"class":52},[46,329,330],{"class":102}," 'shipped'",[46,332,319],{"class":52},[46,334,335],{"class":102}," 'On its way'\n",[46,337,339,341,344,346],{"class":48,"line":338},20,[46,340,197],{"class":52},[46,342,343],{"class":102}," 'delivered'",[46,345,209],{"class":52},[46,347,348],{"class":102}," 'Delivered'\n",[46,350,352,354,357,359],{"class":48,"line":351},21,[46,353,197],{"class":52},[46,355,356],{"class":102}," 'cancelled'",[46,358,209],{"class":52},[46,360,361],{"class":102}," 'Cancelled'\n",[46,363,365,367,370,372],{"class":48,"line":364},22,[46,366,252],{"class":52},[46,368,369],{"class":102}," 'Unknown status: '",[46,371,99],{"class":52},[46,373,308],{"class":52},[46,375,377,379,381],{"class":48,"line":376},23,[46,378,261],{"class":52},[46,380,264],{"class":52},[46,382,383],{"class":64}," status_label\n",[46,385,387,389],{"class":48,"line":386},24,[46,388,273],{"class":52},[46,390,276],{"class":64},[10,392,394],{"id":393},"case-for-custom-sort-order","CASE for custom sort order",[37,396,398],{"className":39,"code":397,"language":41,"meta":42,"style":42},"-- Prioritise urgent tickets; within each priority, oldest first\nSELECT id, title, priority, created_at\nFROM   support_tickets\nWHERE  status = 'open'\nORDER  BY CASE priority\n            WHEN 'critical' THEN 1\n            WHEN 'high'     THEN 2\n            WHEN 'medium'   THEN 3\n            ELSE 4\n          END,\n          created_at ASC;\n",[18,399,400,405,412,419,431,442,455,468,480,488,495],{"__ignoreMap":42},[46,401,402],{"class":48,"line":49},[46,403,404],{"class":68},"-- Prioritise urgent tickets; within each priority, oldest first\n",[46,406,407,409],{"class":48,"line":72},[46,408,53],{"class":52},[46,410,411],{"class":64}," id, title, priority, created_at\n",[46,413,414,416],{"class":48,"line":92},[46,415,273],{"class":52},[46,417,418],{"class":64},"   support_tickets\n",[46,420,421,423,426,428],{"class":48,"line":112},[46,422,149],{"class":52},[46,424,425],{"class":52},"  status",[46,427,59],{"class":52},[46,429,430],{"class":102}," 'open'\n",[46,432,433,436,439],{"class":48,"line":182},[46,434,435],{"class":52},"ORDER  BY",[46,437,438],{"class":52}," CASE",[46,440,441],{"class":64}," priority\n",[46,443,444,447,450,452],{"class":48,"line":188},[46,445,446],{"class":52},"            WHEN",[46,448,449],{"class":102}," 'critical'",[46,451,209],{"class":52},[46,453,454],{"class":82}," 1\n",[46,456,457,459,462,465],{"class":48,"line":194},[46,458,446],{"class":52},[46,460,461],{"class":102}," 'high'",[46,463,464],{"class":52},"     THEN",[46,466,467],{"class":82}," 2\n",[46,469,470,472,475,477],{"class":48,"line":215},[46,471,446],{"class":52},[46,473,474],{"class":102}," 'medium'",[46,476,319],{"class":52},[46,478,479],{"class":82}," 3\n",[46,481,482,485],{"class":48,"line":231},[46,483,484],{"class":52},"            ELSE",[46,486,487],{"class":82}," 4\n",[46,489,490,493],{"class":48,"line":249},[46,491,492],{"class":52},"          END",[46,494,150],{"class":64},[46,496,497,500,503],{"class":48,"line":258},[46,498,499],{"class":64},"          created_at ",[46,501,502],{"class":52},"ASC",[46,504,505],{"class":64},";\n",[10,507,509],{"id":508},"coalesce-the-null-default","COALESCE — the NULL default",[15,511,512,515],{},[18,513,514],{},"COALESCE(a, b, c, ...)"," returns the first non-NULL argument. It is the\nstandard way to supply a fallback when a column might be NULL.",[37,517,519],{"className":39,"code":518,"language":41,"meta":42,"style":42},"-- Show 'N\u002FA' when phone is not provided\nSELECT\n    customer_id,\n    COALESCE(phone, 'N\u002FA')           AS phone,\n    COALESCE(discount, 0)            AS discount,\n    COALESCE(nickname, first_name)   AS display_name\nFROM customers;\n\n-- Prevent NULL in arithmetic (NULL * quantity = NULL for the whole line)\nSELECT\n    product_id,\n    unit_price * COALESCE(quantity, 0) AS line_total\nFROM cart_items;\n\n-- Pick the first available contact method\nSELECT\n    COALESCE(preferred_email, work_email, personal_email, 'no-email') AS contact\nFROM employees;\n",[18,520,521,526,530,534,554,572,584,591,595,600,604,609,632,639,643,648,652,669],{"__ignoreMap":42},[46,522,523],{"class":48,"line":49},[46,524,525],{"class":68},"-- Show 'N\u002FA' when phone is not provided\n",[46,527,528],{"class":48,"line":72},[46,529,169],{"class":52},[46,531,532],{"class":48,"line":92},[46,533,179],{"class":64},[46,535,536,539,542,545,548,551],{"class":48,"line":112},[46,537,538],{"class":82},"    COALESCE",[46,540,541],{"class":64},"(phone, ",[46,543,544],{"class":102},"'N\u002FA'",[46,546,547],{"class":64},")           ",[46,549,550],{"class":52},"AS",[46,552,553],{"class":64}," phone,\n",[46,555,556,558,561,564,567,569],{"class":48,"line":182},[46,557,538],{"class":82},[46,559,560],{"class":64},"(discount, ",[46,562,563],{"class":82},"0",[46,565,566],{"class":64},")            ",[46,568,550],{"class":52},[46,570,571],{"class":64}," discount,\n",[46,573,574,576,579,581],{"class":48,"line":188},[46,575,538],{"class":82},[46,577,578],{"class":64},"(nickname, first_name)   ",[46,580,550],{"class":52},[46,582,583],{"class":64}," display_name\n",[46,585,586,588],{"class":48,"line":194},[46,587,273],{"class":52},[46,589,590],{"class":64}," customers;\n",[46,592,593],{"class":48,"line":215},[46,594,283],{"emptyLinePlaceholder":282},[46,596,597],{"class":48,"line":231},[46,598,599],{"class":68},"-- Prevent NULL in arithmetic (NULL * quantity = NULL for the whole line)\n",[46,601,602],{"class":48,"line":249},[46,603,169],{"class":52},[46,605,606],{"class":48,"line":258},[46,607,608],{"class":64},"    product_id,\n",[46,610,611,614,617,619,622,624,627,629],{"class":48,"line":270},[46,612,613],{"class":64},"    unit_price ",[46,615,616],{"class":52},"*",[46,618,117],{"class":82},[46,620,621],{"class":64},"(quantity, ",[46,623,563],{"class":82},[46,625,626],{"class":64},") ",[46,628,550],{"class":52},[46,630,631],{"class":64}," line_total\n",[46,633,634,636],{"class":48,"line":279},[46,635,273],{"class":52},[46,637,638],{"class":64}," cart_items;\n",[46,640,641],{"class":48,"line":286},[46,642,283],{"emptyLinePlaceholder":282},[46,644,645],{"class":48,"line":292},[46,646,647],{"class":68},"-- Pick the first available contact method\n",[46,649,650],{"class":48,"line":297},[46,651,169],{"class":52},[46,653,654,656,659,662,664,666],{"class":48,"line":302},[46,655,538],{"class":82},[46,657,658],{"class":64},"(preferred_email, work_email, personal_email, ",[46,660,661],{"class":102},"'no-email'",[46,663,626],{"class":64},[46,665,550],{"class":52},[46,667,668],{"class":64}," contact\n",[46,670,671,673],{"class":48,"line":311},[46,672,273],{"class":52},[46,674,675],{"class":64}," employees;\n",[10,677,679],{"id":678},"nullif-converting-a-specific-value-to-null","NULLIF — converting a specific value to NULL",[15,681,682,685],{},[18,683,684],{},"NULLIF(a, b)"," returns NULL if a equals b, otherwise returns a. It is most\nuseful to prevent division-by-zero errors.",[37,687,689],{"className":39,"code":688,"language":41,"meta":42,"style":42},"-- Conversion rate: avoid dividing by zero if no impressions\nSELECT\n    campaign_id,\n    clicks,\n    impressions,\n    ROUND(100.0 * clicks \u002F NULLIF(impressions, 0), 2) AS ctr_pct\nFROM ad_campaigns;\n\n-- Treat empty strings the same as NULL\nSELECT COALESCE(NULLIF(TRIM(notes), ''), 'No notes') AS notes\nFROM support_tickets;\n-- Empty or whitespace-only notes → 'No notes'\n",[18,690,691,696,700,705,710,715,760,767,771,776,810,817],{"__ignoreMap":42},[46,692,693],{"class":48,"line":49},[46,694,695],{"class":68},"-- Conversion rate: avoid dividing by zero if no impressions\n",[46,697,698],{"class":48,"line":72},[46,699,169],{"class":52},[46,701,702],{"class":48,"line":92},[46,703,704],{"class":64},"    campaign_id,\n",[46,706,707],{"class":48,"line":112},[46,708,709],{"class":64},"    clicks,\n",[46,711,712],{"class":48,"line":182},[46,713,714],{"class":64},"    impressions,\n",[46,716,717,720,722,725,728,730,733,736,739,742,745,747,750,753,755,757],{"class":48,"line":188},[46,718,719],{"class":82},"    ROUND",[46,721,120],{"class":64},[46,723,724],{"class":82},"100",[46,726,727],{"class":64},".",[46,729,563],{"class":82},[46,731,732],{"class":52}," *",[46,734,735],{"class":64}," clicks ",[46,737,738],{"class":52},"\u002F",[46,740,741],{"class":82}," NULLIF",[46,743,744],{"class":64},"(impressions, ",[46,746,563],{"class":82},[46,748,749],{"class":64},"), ",[46,751,752],{"class":82},"2",[46,754,626],{"class":64},[46,756,550],{"class":52},[46,758,759],{"class":64}," ctr_pct\n",[46,761,762,764],{"class":48,"line":194},[46,763,273],{"class":52},[46,765,766],{"class":64}," ad_campaigns;\n",[46,768,769],{"class":48,"line":215},[46,770,283],{"emptyLinePlaceholder":282},[46,772,773],{"class":48,"line":231},[46,774,775],{"class":68},"-- Treat empty strings the same as NULL\n",[46,777,778,780,782,784,787,789,792,795,798,800,803,805,807],{"class":48,"line":249},[46,779,53],{"class":52},[46,781,117],{"class":82},[46,783,120],{"class":64},[46,785,786],{"class":82},"NULLIF",[46,788,120],{"class":64},[46,790,791],{"class":82},"TRIM",[46,793,794],{"class":64},"(notes), ",[46,796,797],{"class":102},"''",[46,799,749],{"class":64},[46,801,802],{"class":102},"'No notes'",[46,804,626],{"class":64},[46,806,550],{"class":52},[46,808,809],{"class":64}," notes\n",[46,811,812,814],{"class":48,"line":258},[46,813,273],{"class":52},[46,815,816],{"class":64}," support_tickets;\n",[46,818,819],{"class":48,"line":270},[46,820,821],{"class":68},"-- Empty or whitespace-only notes → 'No notes'\n",[10,823,825],{"id":824},"conditional-aggregation-pivoting-data","Conditional aggregation — pivoting data",[15,827,828,829,831],{},"Combining ",[18,830,143],{}," with an aggregate function computes multiple metrics in a\nsingle pass — no subqueries or UNION needed.",[37,833,835],{"className":39,"code":834,"language":41,"meta":42,"style":42},"-- Monthly orders broken down by status in one query\nSELECT\n    DATE_TRUNC('month', created_at)                             AS month,\n    COUNT(*)                                                    AS total,\n    COUNT(CASE WHEN status = 'completed' THEN 1 END)            AS completed,\n    COUNT(CASE WHEN status = 'cancelled' THEN 1 END)            AS cancelled,\n    COUNT(CASE WHEN status = 'refunded'  THEN 1 END)            AS refunded,\n    SUM(CASE WHEN status = 'completed' THEN total_amount ELSE 0 END) AS revenue\nFROM orders\nGROUP BY DATE_TRUNC('month', created_at)\nORDER BY month DESC;\n",[18,836,837,842,846,864,881,915,944,975,1011,1018,1031],{"__ignoreMap":42},[46,838,839],{"class":48,"line":49},[46,840,841],{"class":68},"-- Monthly orders broken down by status in one query\n",[46,843,844],{"class":48,"line":72},[46,845,169],{"class":52},[46,847,848,851,854,857,859,862],{"class":48,"line":92},[46,849,850],{"class":64},"    DATE_TRUNC(",[46,852,853],{"class":102},"'month'",[46,855,856],{"class":64},", created_at)                             ",[46,858,550],{"class":52},[46,860,861],{"class":52}," month",[46,863,150],{"class":64},[46,865,866,869,871,873,876,878],{"class":48,"line":112},[46,867,868],{"class":82},"    COUNT",[46,870,120],{"class":64},[46,872,616],{"class":52},[46,874,875],{"class":64},")                                                    ",[46,877,550],{"class":52},[46,879,880],{"class":64}," total,\n",[46,882,883,885,887,889,892,895,897,900,902,905,908,910,912],{"class":48,"line":182},[46,884,868],{"class":82},[46,886,120],{"class":64},[46,888,143],{"class":52},[46,890,891],{"class":52}," WHEN",[46,893,894],{"class":52}," status",[46,896,59],{"class":52},[46,898,899],{"class":102}," 'completed'",[46,901,209],{"class":52},[46,903,904],{"class":82}," 1",[46,906,907],{"class":52}," END",[46,909,566],{"class":64},[46,911,550],{"class":52},[46,913,914],{"class":64}," completed,\n",[46,916,917,919,921,923,925,927,929,931,933,935,937,939,941],{"class":48,"line":188},[46,918,868],{"class":82},[46,920,120],{"class":64},[46,922,143],{"class":52},[46,924,891],{"class":52},[46,926,894],{"class":52},[46,928,59],{"class":52},[46,930,356],{"class":102},[46,932,209],{"class":52},[46,934,904],{"class":82},[46,936,907],{"class":52},[46,938,566],{"class":64},[46,940,550],{"class":52},[46,942,943],{"class":64}," cancelled,\n",[46,945,946,948,950,952,954,956,958,961,964,966,968,970,972],{"class":48,"line":194},[46,947,868],{"class":82},[46,949,120],{"class":64},[46,951,143],{"class":52},[46,953,891],{"class":52},[46,955,894],{"class":52},[46,957,59],{"class":52},[46,959,960],{"class":102}," 'refunded'",[46,962,963],{"class":52},"  THEN",[46,965,904],{"class":82},[46,967,907],{"class":52},[46,969,566],{"class":64},[46,971,550],{"class":52},[46,973,974],{"class":64}," refunded,\n",[46,976,977,980,982,984,986,988,990,992,994,996,999,1002,1004,1006,1008],{"class":48,"line":215},[46,978,979],{"class":82},"    SUM",[46,981,120],{"class":64},[46,983,143],{"class":52},[46,985,891],{"class":52},[46,987,894],{"class":52},[46,989,59],{"class":52},[46,991,899],{"class":102},[46,993,209],{"class":52},[46,995,200],{"class":64},[46,997,998],{"class":52},"ELSE",[46,1000,1001],{"class":82}," 0",[46,1003,907],{"class":52},[46,1005,626],{"class":64},[46,1007,550],{"class":52},[46,1009,1010],{"class":64}," revenue\n",[46,1012,1013,1015],{"class":48,"line":231},[46,1014,273],{"class":52},[46,1016,1017],{"class":64}," orders\n",[46,1019,1020,1023,1026,1028],{"class":48,"line":249},[46,1021,1022],{"class":52},"GROUP BY",[46,1024,1025],{"class":64}," DATE_TRUNC(",[46,1027,853],{"class":102},[46,1029,1030],{"class":64},", created_at)\n",[46,1032,1033,1035,1037,1040],{"class":48,"line":258},[46,1034,153],{"class":52},[46,1036,861],{"class":52},[46,1038,1039],{"class":52}," DESC",[46,1041,505],{"class":64},[15,1043,1044,1045,1048],{},"Postgres shorthand with ",[18,1046,1047],{},"FILTER",":",[37,1050,1052],{"className":39,"code":1051,"language":41,"meta":42,"style":42},"COUNT(*) FILTER (WHERE status = 'completed') AS completed\n",[18,1053,1054],{"__ignoreMap":42},[46,1055,1056,1059,1061,1063,1065,1067,1070,1072,1074,1076,1078,1080,1082],{"class":48,"line":49},[46,1057,1058],{"class":82},"COUNT",[46,1060,120],{"class":64},[46,1062,616],{"class":52},[46,1064,626],{"class":64},[46,1066,1047],{"class":52},[46,1068,1069],{"class":64}," (",[46,1071,149],{"class":52},[46,1073,894],{"class":52},[46,1075,59],{"class":52},[46,1077,899],{"class":102},[46,1079,626],{"class":64},[46,1081,550],{"class":52},[46,1083,1084],{"class":64}," completed\n",[10,1086,1088],{"id":1087},"not-in-with-nulls-the-silent-zero-rows-trap","NOT IN with NULLs — the silent zero-rows trap",[37,1090,1092],{"className":39,"code":1091,"language":41,"meta":42,"style":42},"-- Suppose discontinued_products has a NULL product_id row\nSELECT * FROM products\nWHERE id NOT IN (SELECT product_id FROM discontinued_products);\n-- Returns ZERO rows if any product_id in the subquery is NULL\n-- Because: 1 \u003C> NULL is UNKNOWN → NOT IN returns no rows at all\n\n-- Safe alternative: NOT EXISTS\nSELECT * FROM products p\nWHERE NOT EXISTS (\n    SELECT 1 FROM discontinued_products d WHERE d.product_id = p.id\n);\n-- Correctly returns all non-discontinued products\n",[18,1093,1094,1099,1111,1136,1141,1146,1150,1155,1166,1179,1211,1216],{"__ignoreMap":42},[46,1095,1096],{"class":48,"line":49},[46,1097,1098],{"class":68},"-- Suppose discontinued_products has a NULL product_id row\n",[46,1100,1101,1103,1105,1108],{"class":48,"line":72},[46,1102,53],{"class":52},[46,1104,732],{"class":52},[46,1106,1107],{"class":52}," FROM",[46,1109,1110],{"class":64}," products\n",[46,1112,1113,1115,1118,1121,1124,1126,1128,1131,1133],{"class":48,"line":92},[46,1114,149],{"class":52},[46,1116,1117],{"class":64}," id ",[46,1119,1120],{"class":52},"NOT",[46,1122,1123],{"class":52}," IN",[46,1125,1069],{"class":64},[46,1127,53],{"class":52},[46,1129,1130],{"class":64}," product_id ",[46,1132,273],{"class":52},[46,1134,1135],{"class":64}," discontinued_products);\n",[46,1137,1138],{"class":48,"line":112},[46,1139,1140],{"class":68},"-- Returns ZERO rows if any product_id in the subquery is NULL\n",[46,1142,1143],{"class":48,"line":182},[46,1144,1145],{"class":68},"-- Because: 1 \u003C> NULL is UNKNOWN → NOT IN returns no rows at all\n",[46,1147,1148],{"class":48,"line":188},[46,1149,283],{"emptyLinePlaceholder":282},[46,1151,1152],{"class":48,"line":194},[46,1153,1154],{"class":68},"-- Safe alternative: NOT EXISTS\n",[46,1156,1157,1159,1161,1163],{"class":48,"line":215},[46,1158,53],{"class":52},[46,1160,732],{"class":52},[46,1162,1107],{"class":52},[46,1164,1165],{"class":64}," products p\n",[46,1167,1168,1170,1173,1176],{"class":48,"line":231},[46,1169,149],{"class":52},[46,1171,1172],{"class":52}," NOT",[46,1174,1175],{"class":52}," EXISTS",[46,1177,1178],{"class":64}," (\n",[46,1180,1181,1184,1186,1188,1191,1193,1196,1198,1201,1203,1206,1208],{"class":48,"line":249},[46,1182,1183],{"class":52},"    SELECT",[46,1185,904],{"class":82},[46,1187,1107],{"class":52},[46,1189,1190],{"class":64}," discontinued_products d ",[46,1192,149],{"class":52},[46,1194,1195],{"class":82}," d",[46,1197,727],{"class":64},[46,1199,1200],{"class":82},"product_id",[46,1202,59],{"class":52},[46,1204,1205],{"class":82}," p",[46,1207,727],{"class":64},[46,1209,1210],{"class":82},"id\n",[46,1212,1213],{"class":48,"line":258},[46,1214,1215],{"class":64},");\n",[46,1217,1218],{"class":48,"line":270},[46,1219,1220],{"class":68},"-- Correctly returns all non-discontinued products\n",[10,1222,1224],{"id":1223},"is-distinct-from-null-safe-equality","IS DISTINCT FROM — NULL-safe equality",[15,1226,1227,1228,1231,1232,1235],{},"Standard ",[18,1229,1230],{},"="," returns UNKNOWN when either side is NULL. Use ",[18,1233,1234],{},"IS NOT DISTINCT FROM","\n(Postgres) for NULL-safe equality:",[37,1237,1239],{"className":39,"code":1238,"language":41,"meta":42,"style":42},"-- Find rows where the shipping address changed (may be NULL on either side)\nSELECT order_id\nFROM order_history\nWHERE old_shipping_address IS DISTINCT FROM new_shipping_address;\n-- Returns rows where values genuinely differ, including NULL vs non-NULL changes\n-- NULL IS DISTINCT FROM NULL → FALSE (they are the same)\n",[18,1240,1241,1246,1253,1260,1278,1283],{"__ignoreMap":42},[46,1242,1243],{"class":48,"line":49},[46,1244,1245],{"class":68},"-- Find rows where the shipping address changed (may be NULL on either side)\n",[46,1247,1248,1250],{"class":48,"line":72},[46,1249,53],{"class":52},[46,1251,1252],{"class":64}," order_id\n",[46,1254,1255,1257],{"class":48,"line":92},[46,1256,273],{"class":52},[46,1258,1259],{"class":64}," order_history\n",[46,1261,1262,1264,1267,1270,1273,1275],{"class":48,"line":112},[46,1263,149],{"class":52},[46,1265,1266],{"class":64}," old_shipping_address ",[46,1268,1269],{"class":52},"IS",[46,1271,1272],{"class":52}," DISTINCT",[46,1274,1107],{"class":52},[46,1276,1277],{"class":64}," new_shipping_address;\n",[46,1279,1280],{"class":48,"line":182},[46,1281,1282],{"class":68},"-- Returns rows where values genuinely differ, including NULL vs non-NULL changes\n",[46,1284,1285],{"class":48,"line":188},[46,1286,1287],{"class":68},"-- NULL IS DISTINCT FROM NULL → FALSE (they are the same)\n",[10,1289,1291],{"id":1290},"greatest-and-least","GREATEST and LEAST",[37,1293,1295],{"className":39,"code":1294,"language":41,"meta":42,"style":42},"-- Clamp a user-supplied rating to the valid 1–5 range\nSELECT\n    user_id,\n    raw_rating,\n    LEAST(GREATEST(raw_rating, 1), 5) AS clamped_rating\nFROM feedback;\n\n-- Find the later of two dates\nSELECT\n    order_id,\n    GREATEST(shipped_at, returned_at) AS last_event\nFROM orders;\n",[18,1296,1297,1302,1306,1311,1316,1344,1351,1355,1360,1364,1369,1382],{"__ignoreMap":42},[46,1298,1299],{"class":48,"line":49},[46,1300,1301],{"class":68},"-- Clamp a user-supplied rating to the valid 1–5 range\n",[46,1303,1304],{"class":48,"line":72},[46,1305,169],{"class":52},[46,1307,1308],{"class":48,"line":92},[46,1309,1310],{"class":64},"    user_id,\n",[46,1312,1313],{"class":48,"line":112},[46,1314,1315],{"class":64},"    raw_rating,\n",[46,1317,1318,1321,1323,1326,1329,1332,1334,1337,1339,1341],{"class":48,"line":182},[46,1319,1320],{"class":82},"    LEAST",[46,1322,120],{"class":64},[46,1324,1325],{"class":82},"GREATEST",[46,1327,1328],{"class":64},"(raw_rating, ",[46,1330,1331],{"class":82},"1",[46,1333,749],{"class":64},[46,1335,1336],{"class":82},"5",[46,1338,626],{"class":64},[46,1340,550],{"class":52},[46,1342,1343],{"class":64}," clamped_rating\n",[46,1345,1346,1348],{"class":48,"line":188},[46,1347,273],{"class":52},[46,1349,1350],{"class":64}," feedback;\n",[46,1352,1353],{"class":48,"line":194},[46,1354,283],{"emptyLinePlaceholder":282},[46,1356,1357],{"class":48,"line":215},[46,1358,1359],{"class":68},"-- Find the later of two dates\n",[46,1361,1362],{"class":48,"line":231},[46,1363,169],{"class":52},[46,1365,1366],{"class":48,"line":249},[46,1367,1368],{"class":64},"    order_id,\n",[46,1370,1371,1374,1377,1379],{"class":48,"line":258},[46,1372,1373],{"class":82},"    GREATEST",[46,1375,1376],{"class":64},"(shipped_at, returned_at) ",[46,1378,550],{"class":52},[46,1380,1381],{"class":64}," last_event\n",[46,1383,1384,1386],{"class":48,"line":270},[46,1385,273],{"class":52},[46,1387,276],{"class":64},[10,1389,1391],{"id":1390},"recap","Recap",[15,1393,1394,1396,1397,1400,1401,1404,1405,1408,1409,1412,1413,1415,1416,1418,1419,1422,1423,1426,1427,1429],{},[18,1395,20],{}," is not zero — test for it with ",[18,1398,1399],{},"IS NULL"," \u002F ",[18,1402,1403],{},"IS NOT NULL",", never with\n",[18,1406,1407],{},"= NULL",". Use ",[18,1410,1411],{},"COALESCE"," to provide defaults; ",[18,1414,786],{}," to convert a sentinel\nvalue to NULL (especially before division). Use ",[18,1417,143],{}," inside aggregate\nfunctions for in-query pivoting. Never use ",[18,1420,1421],{},"NOT IN (subquery)"," when the\nsubquery might return NULLs — use ",[18,1424,1425],{},"NOT EXISTS"," instead. ",[18,1428,1234],{},"\nis the NULL-safe equality test for upsert and change-detection queries.",[1431,1432,1433],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":42,"searchDepth":72,"depth":72,"links":1435},[1436,1437,1438,1439,1440,1441,1442,1443,1444,1445],{"id":12,"depth":72,"text":13},{"id":137,"depth":72,"text":138},{"id":393,"depth":72,"text":394},{"id":508,"depth":72,"text":509},{"id":678,"depth":72,"text":679},{"id":824,"depth":72,"text":825},{"id":1087,"depth":72,"text":1088},{"id":1223,"depth":72,"text":1224},{"id":1290,"depth":72,"text":1291},{"id":1390,"depth":72,"text":1391},"SQL CASE expressions, COALESCE, NULLIF, conditional aggregation, and NULL logic explained — with real examples for data cleaning, pivoting, and reporting.","medium","md","SQL",{},"\u002Fblog\u002Fsql-case-coalesce-null-handling","\u002Fsql\u002Ffunctions\u002Fconditional-null-functions",{"title":5,"description":1446},"blog\u002Fsql-case-coalesce-null-handling","Conditional & NULL Functions","Built-in Functions","functions","2026-06-20","-_Ebgd9zMy-utFpRRRfgQRxoybOvlie6nxzuh5D7Tj0",1782244088702]