[{"data":1,"prerenderedAt":107},["ShallowReactive",2],{"qa-\u002Fsql\u002Ffunctions\u002Fconditional-null-functions":3},{"page":4,"siblings":95,"blog":104},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":85,"related":86,"seo":87,"seoDescription":88,"stem":89,"subtopic":90,"topic":91,"topicSlug":92,"updated":93,"__hash__":94},"qa\u002Fsql\u002Ffunctions\u002Fconditional-null-functions.md","Conditional Null Functions",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","SQL","sql",{},true,3,"\u002Fsql\u002Ffunctions\u002Fconditional-null-functions",[23,28,32,36,40,44,48,52,56,60,64,69,73,77,81],{"id":24,"difficulty":25,"q":26,"a":27},"case-expression","easy","What is the CASE expression and what are its two forms?","`CASE` is SQL's conditional expression — an inline if\u002Felse that returns a\nvalue. It has two forms:\n\n**Simple CASE** — compares one expression to several values:\n\n```sql\nSELECT order_id,\n       CASE status\n         WHEN 'pending'   THEN 'Awaiting payment'\n         WHEN 'shipped'   THEN 'On its way'\n         WHEN 'delivered' THEN 'Complete'\n         ELSE 'Unknown'\n       END AS status_label\nFROM orders;\n```\n\n**Searched CASE** — evaluates independent boolean conditions:\n\n```sql\nSELECT order_id, total,\n       CASE\n         WHEN total >= 500 THEN 'Large'\n         WHEN total >= 100 THEN 'Medium'\n         ELSE 'Small'\n       END AS size_category\nFROM orders;\n```\n\n`CASE` can appear anywhere an expression is valid: `SELECT`, `WHERE`,\n`ORDER BY`, `GROUP BY`, inside aggregate functions.\n\n**Rule of thumb:** use the simple form for equality checks on a single\ncolumn; use the searched form when conditions involve different columns,\ncomparisons, or `IS NULL` checks.\n",{"id":29,"difficulty":25,"q":30,"a":31},"coalesce","What does COALESCE do?","`COALESCE(expr1, expr2, …)` returns the **first non-NULL value** from its\nargument list. It is the standard way to substitute a default for a NULL.\n\n```sql\n-- Use a fallback when a column might be NULL\nSELECT COALESCE(phone, 'N\u002FA')          AS phone    FROM users;\nSELECT COALESCE(discount, 0)           AS discount FROM orders;\nSELECT COALESCE(nickname, first_name)  AS display  FROM users;\n\n-- Chain multiple fallbacks\nSELECT COALESCE(preferred_email, work_email, personal_email, 'no-email@unknown.com')\nFROM contacts;\n\n-- Avoid NULL in arithmetic (NULL + anything = NULL)\nSELECT price * COALESCE(quantity, 0) AS line_total FROM cart_items;\n```\n\n`COALESCE` short-circuits: it stops evaluating arguments as soon as it\nfinds a non-NULL value. All arguments must be type-compatible.\n\n**Rule of thumb:** use `COALESCE` to provide defaults for nullable columns.\nIt is cleaner and more portable than `CASE WHEN col IS NULL THEN default\nELSE col END` — they are exactly equivalent.\n",{"id":33,"difficulty":14,"q":34,"a":35},"nullif","What does NULLIF do and when is it useful?","`NULLIF(expr1, expr2)` returns `NULL` if the two expressions are equal,\notherwise returns `expr1`. It is the inverse of `COALESCE` — converting a\nspecific value to `NULL`.\n\n```sql\n-- Prevent division-by-zero: replace 0 with NULL before dividing\nSELECT numerator \u002F NULLIF(denominator, 0) AS ratio FROM metrics;\n-- If denominator = 0 → NULL instead of ERROR\n\n-- Convert a sentinel value to NULL\nSELECT NULLIF(phone, 'N\u002FA') AS phone FROM contacts;\n-- 'N\u002FA' → NULL; other values pass through unchanged\n\n-- Combine with COALESCE for clean defaults\nSELECT COALESCE(NULLIF(TRIM(notes), ''), 'No notes') AS notes FROM tickets;\n-- Empty-string or whitespace-only → 'No notes'\n```\n\n**Rule of thumb:** reach for `NULLIF(col, 0)` any time you are dividing\nby a column that may be zero. Pair with `COALESCE` when you also want to\nreplace the resulting NULL with a default.\n",{"id":37,"difficulty":25,"q":38,"a":39},"ifnull-nvl-isnull","What are IFNULL, NVL, and ISNULL — and how do they compare to COALESCE?","These are two-argument shortcuts for replacing NULL with a default — they\nare equivalent to `COALESCE(expr, default)` but are database-specific:\n\n| Function | Database |\n|---|---|\n| `IFNULL(expr, default)` | MySQL |\n| `NVL(expr, default)` | Oracle |\n| `ISNULL(expr, default)` | SQL Server \u002F Sybase |\n| `COALESCE(expr, default)` | All (ANSI SQL) |\n\n```sql\n-- MySQL\nSELECT IFNULL(discount, 0) FROM orders;\n\n-- SQL Server\nSELECT ISNULL(discount, 0) FROM orders;\n\n-- COALESCE — works everywhere and accepts more than two arguments\nSELECT COALESCE(discount, 0) FROM orders;\n```\n\n**Rule of thumb:** use `COALESCE` in any code that needs to be portable.\nUse `IFNULL`\u002F`ISNULL` only in database-specific stored procedures where\nportability is not a concern. Avoid `NVL` outside Oracle.\n",{"id":41,"difficulty":25,"q":42,"a":43},"iif","What is IIF and when can you use it?","`IIF(condition, true_value, false_value)` is a compact inline if\u002Felse\navailable in **SQL Server** and **MySQL** (as `IF`). It is syntactic sugar\nfor a two-branch `CASE`.\n\n```sql\n-- SQL Server IIF\nSELECT order_id,\n       IIF(total > 100, 'Large', 'Small') AS size\nFROM orders;\n\n-- MySQL IF (same concept, different name)\nSELECT order_id,\n       IF(total > 100, 'Large', 'Small') AS size\nFROM orders;\n\n-- Equivalent CASE (works everywhere)\nSELECT order_id,\n       CASE WHEN total > 100 THEN 'Large' ELSE 'Small' END AS size\nFROM orders;\n```\n\n**Rule of thumb:** use `CASE` for portability. Use `IIF` (SQL Server) or\n`IF` (MySQL) in database-specific scripts where you prefer the terser\nsyntax, but be aware that porting the query later requires rewriting it.\n",{"id":45,"difficulty":14,"q":46,"a":47},"null-in-comparisons","Why do comparisons with NULL often return unexpected results?","`NULL` represents an unknown value. Any comparison involving `NULL` evaluates\nto **UNKNOWN** (not TRUE or FALSE) under SQL's three-valued logic. Since\n`WHERE` clauses only keep rows where the condition is TRUE, rows with NULL\nin a comparison are silently excluded.\n\n```sql\n-- All of these evaluate to UNKNOWN, not TRUE:\nSELECT NULL = NULL;      -- UNKNOWN\nSELECT NULL \u003C> 1;        -- UNKNOWN\nSELECT NULL > 0;         -- UNKNOWN\nSELECT 1 = NULL;         -- UNKNOWN\n\n-- Correct tests for NULL\nSELECT NULL IS NULL;     -- TRUE\nSELECT NULL IS NOT NULL; -- FALSE\n\n-- Common mistake: missing IS NULL rows\nSELECT * FROM orders WHERE discount \u003C> 0;\n-- Excludes rows where discount IS NULL — those rows have unknown discount\n\n-- Fix: explicitly include the NULL case\nSELECT * FROM orders WHERE discount \u003C> 0 OR discount IS NULL;\n```\n\n**Rule of thumb:** whenever a column may be NULL, check whether your\n`WHERE` clause correctly handles it. Any condition using `=`, `\u003C>`, `>`,\n`\u003C`, `LIKE`, or `IN` silently drops NULL rows.\n",{"id":49,"difficulty":14,"q":50,"a":51},"null-in-aggregates","How does NULL affect aggregate functions?","All aggregate functions (except `COUNT(*)`) **ignore NULL values** in their\ninput. This is usually what you want, but it can produce surprising results.\n\n```sql\n-- Setup: discount column has some NULLs\n-- | id | total | discount |\n-- | 1  | 100   | 10       |\n-- | 2  | 200   | NULL     |\n-- | 3  | 300   | 20       |\n\nSELECT COUNT(*)        AS total_rows,       -- → 3 (counts all rows)\n       COUNT(discount) AS non_null_discount, -- → 2 (skips NULLs)\n       SUM(discount)   AS total_discount,    -- → 30 (NULLs ignored)\n       AVG(discount)   AS avg_discount       -- → 15 (30 \u002F 2, not 30 \u002F 3!)\nFROM orders;\n-- AVG denominator is the COUNT of non-NULL rows, not total rows.\n\n-- Fix: use COALESCE to treat NULL as 0 in AVG\nSELECT AVG(COALESCE(discount, 0)) AS avg_discount FROM orders; -- → 10\n```\n\n**Rule of thumb:** `AVG(col)` divides by the count of non-NULL values —\nthis can silently exclude missing data from the average. When NULLs should\nbe treated as 0 in averages, use `AVG(COALESCE(col, 0))`.\n",{"id":53,"difficulty":14,"q":54,"a":55},"conditional-aggregation","What is conditional aggregation and how do you use it?","**Conditional aggregation** uses a `CASE` expression inside an aggregate\nfunction to count, sum, or average only rows matching a condition — pivoting\nrow data into columns without a JOIN.\n\n```sql\n-- Count orders by status in a single pass\nSELECT COUNT(*)                                         AS total,\n       COUNT(CASE WHEN status = 'pending'   THEN 1 END) AS pending,\n       COUNT(CASE WHEN status = 'shipped'   THEN 1 END) AS shipped,\n       COUNT(CASE WHEN status = 'delivered' THEN 1 END) AS delivered,\n       SUM(CASE WHEN status = 'pending' THEN total ELSE 0 END) AS pending_revenue\nFROM orders;\n\n-- Postgres \u002F SQL Server shorthand: FILTER clause\nSELECT COUNT(*) FILTER (WHERE status = 'pending')   AS pending,\n       COUNT(*) FILTER (WHERE status = 'shipped')   AS shipped,\n       SUM(total) FILTER (WHERE status = 'pending') AS pending_revenue\nFROM orders;\n```\n\n**Rule of thumb:** use conditional aggregation to pivot data in a single\nquery instead of multiple subqueries or UNION. The `FILTER` clause\n(Postgres\u002FSQL Server) is cleaner than `CASE WHEN … THEN 1 END` — prefer\nit when available.\n",{"id":57,"difficulty":25,"q":58,"a":59},"greatest-least","What are GREATEST and LEAST?","`GREATEST(val1, val2, …)` returns the largest value from its arguments.\n`LEAST(val1, val2, …)` returns the smallest. They work across any\ncomparable types and return NULL if any argument is NULL (Postgres\u002FMySQL);\nSQL Server does not have these functions natively.\n\n```sql\nSELECT GREATEST(10, 20, 5);          -- → 20\nSELECT LEAST(10, 20, 5);             -- → 5\nSELECT GREATEST(NULL, 10, 20);       -- → NULL (any NULL propagates)\n\n-- Practical: clamp a value within a min\u002Fmax range\nSELECT LEAST(GREATEST(user_rating, 1), 5) AS clamped_rating FROM reviews;\n-- Ensures rating is always between 1 and 5\n\n-- Use COALESCE to handle NULLs when comparing columns\nSELECT GREATEST(COALESCE(a, 0), COALESCE(b, 0)) AS max_ab FROM t;\n```\n\nSQL Server equivalent using CASE:\n```sql\nSELECT CASE WHEN a > b THEN a ELSE b END AS greatest_ab FROM t;\n```\n\n**Rule of thumb:** use `GREATEST`\u002F`LEAST` for clamping values to a range\nor comparing columns from the same row — they are cleaner than nested\n`CASE` expressions for this pattern.\n",{"id":61,"difficulty":14,"q":62,"a":63},"null-safe-equals","How do you compare two values that may both be NULL?","Standard `=` returns UNKNOWN when either side is NULL. To check whether two\nnullable columns are \"equal\" (including both being NULL), use database-\nspecific NULL-safe equality.\n\n```sql\n-- Standard SQL: verbose but portable\nSELECT *\nFROM   t1\nJOIN   t2 ON (t1.col = t2.col) OR (t1.col IS NULL AND t2.col IS NULL);\n\n-- Postgres: IS NOT DISTINCT FROM (NULL-safe equality)\nSELECT * FROM t1 JOIN t2 ON t1.col IS NOT DISTINCT FROM t2.col;\n-- NULL IS NOT DISTINCT FROM NULL → TRUE\n-- 1   IS NOT DISTINCT FROM 1    → TRUE\n-- 1   IS NOT DISTINCT FROM NULL → FALSE\n\n-- MySQL: \u003C=> (spaceship operator)\nSELECT * FROM t1 JOIN t2 ON t1.col \u003C=> t2.col;\n-- NULL \u003C=> NULL → 1 (TRUE)\n\n-- SQL Server: no shorthand — use the verbose ANSI form\n```\n\n**Rule of thumb:** use `IS NOT DISTINCT FROM` (Postgres) or `\u003C=>` (MySQL)\nwhen joining or comparing nullable columns where two NULLs should be\nconsidered equal. This is common in upsert logic and change-detection\nqueries.\n",{"id":65,"difficulty":66,"q":67,"a":68},"boolean-logic-nulls","hard","How does three-valued logic affect AND and OR with NULLs?","SQL uses three truth values: TRUE, FALSE, and UNKNOWN (NULL). `AND` and\n`OR` follow specific rules when UNKNOWN is involved:\n\n```\nTRUE  AND UNKNOWN = UNKNOWN\nFALSE AND UNKNOWN = FALSE     ← FALSE wins in AND\nTRUE  OR  UNKNOWN = TRUE      ← TRUE wins in OR\nFALSE OR  UNKNOWN = UNKNOWN\nNOT UNKNOWN       = UNKNOWN\n```\n\n```sql\n-- Pitfall: NOT IN with a NULL in the subquery\nSELECT * FROM products\nWHERE  id NOT IN (SELECT product_id FROM discontinued WHERE product_id IS NULL);\n-- If the subquery returns even one NULL, NOT IN always returns UNKNOWN\n-- → zero rows returned! (UNKNOWN is treated as FALSE in WHERE)\n\n-- Fix: use NOT EXISTS instead\nSELECT * FROM products p\nWHERE NOT EXISTS (\n  SELECT 1 FROM discontinued d WHERE d.product_id = p.id\n);\n-- NOT EXISTS correctly handles NULLs — returns TRUE if no match found\n```\n\n**Rule of thumb:** never use `NOT IN (subquery)` when the subquery can\nreturn NULLs — it silently returns zero rows. Always use `NOT EXISTS` as\nthe safe alternative.\n",{"id":70,"difficulty":25,"q":71,"a":72},"decode-decode","How do you handle multiple conditional mappings cleanly?","For mapping one value to another across many cases, `CASE` is the standard\napproach. Some databases also offer compact alternatives.\n\n```sql\n-- Standard CASE — readable and portable\nSELECT status,\n       CASE status\n         WHEN 1 THEN 'Active'\n         WHEN 2 THEN 'Inactive'\n         WHEN 3 THEN 'Banned'\n         ELSE        'Unknown'\n       END AS status_label\nFROM users;\n\n-- Oracle DECODE (not available in other databases)\nSELECT DECODE(status, 1, 'Active', 2, 'Inactive', 3, 'Banned', 'Unknown')\nFROM users;\n\n-- Postgres: use a lookup table or CASE — no DECODE\n-- Alternative: join to a status lookup table (preferred for long lists)\nSELECT u.id, s.label\nFROM users u\nJOIN status_codes s ON s.code = u.status;\n```\n\n**Rule of thumb:** for short, stable mappings (\u003C 6 values), use `CASE`.\nFor longer or changeable mappings, maintain a lookup\u002Freference table and\njoin to it — the mapping is then data, not code, and can be updated without\na schema change.\n",{"id":74,"difficulty":14,"q":75,"a":76},"try-convert","How do you safely cast a string to a number or date without raising an error?","If a string column contains non-numeric values and you try to `CAST` it to\na number, the query fails. SQL Server and MySQL offer safe-cast functions\nthat return NULL on failure. Postgres requires a different approach.\n\n```sql\n-- SQL Server: TRY_CAST \u002F TRY_CONVERT (return NULL on failure)\nSELECT TRY_CAST('123'   AS INT);        -- → 123\nSELECT TRY_CAST('abc'   AS INT);        -- → NULL (no error)\nSELECT TRY_CONVERT(DATE, '2026-06-20'); -- → '2026-06-20'\nSELECT TRY_CONVERT(DATE, 'not-a-date'); -- → NULL\n\n-- MySQL: CAST silently coerces and produces 0 or NULL\nSELECT CAST('abc' AS UNSIGNED);  -- → 0 (silent coercion, with a warning)\n\n-- Postgres: no TRY_CAST built-in; use regexp check before casting\nSELECT CASE\n         WHEN value ~ '^\\d+$' THEN value::INT\n         ELSE NULL\n       END AS safe_int\nFROM input_data;\n-- Or wrap in a PL\u002FpgSQL function that catches exceptions\n```\n\n**Rule of thumb:** validate and sanitise data at the application boundary\nbefore it enters the database. When cleaning dirty data inside SQL, use\n`TRY_CAST` (SQL Server) or a regex guard (Postgres) to avoid query-aborting\ncast errors.\n",{"id":78,"difficulty":14,"q":79,"a":80},"case-in-order-by","How can you use CASE in ORDER BY to create custom sort orders?","`CASE` inside `ORDER BY` lets you define a custom sort priority that is not\npossible with a simple column sort — for example, putting a specific status\nfirst, sorting NULL values to the bottom, or combining multiple conditions.\n\n```sql\n-- Sort 'pending' orders first, then 'shipped', then all others alphabetically\nSELECT order_id, status\nFROM   orders\nORDER  BY CASE status\n            WHEN 'pending' THEN 1\n            WHEN 'shipped' THEN 2\n            ELSE 3\n          END,\n         status ASC;  -- secondary sort within the ELSE group\n\n-- Sort NULLs to the bottom (Postgres also has NULLS LAST natively)\nSELECT id, priority\nFROM   tasks\nORDER  BY CASE WHEN priority IS NULL THEN 1 ELSE 0 END,\n         priority ASC;\n\n-- Postgres native null ordering (cleaner):\nSELECT id, priority FROM tasks ORDER BY priority ASC NULLS LAST;\n```\n\n**Rule of thumb:** use `CASE` in `ORDER BY` when the sort requirement\ncannot be expressed as `ASC`\u002F`DESC` on existing columns. For `NULL` ordering\nspecifically, prefer `NULLS FIRST` \u002F `NULLS LAST` in databases that support\nit (Postgres, Oracle, SQL Server 2022+) — it is more readable.\n",{"id":82,"difficulty":66,"q":83,"a":84},"pivot-with-case","How do you pivot rows into columns using CASE?","**Pivoting** transforms row values into column headers. SQL lacks a native\n`PIVOT` syntax in all databases, but conditional aggregation with `CASE`\nachieves the same result and is portable.\n\n```sql\n-- Data: (year, quarter, revenue)\n-- Goal: one row per year with columns q1, q2, q3, q4\n\nSELECT year,\n       SUM(CASE WHEN quarter = 1 THEN revenue END) AS q1,\n       SUM(CASE WHEN quarter = 2 THEN revenue END) AS q2,\n       SUM(CASE WHEN quarter = 3 THEN revenue END) AS q3,\n       SUM(CASE WHEN quarter = 4 THEN revenue END) AS q4\nFROM   quarterly_revenue\nGROUP  BY year\nORDER  BY year;\n\n-- SQL Server native PIVOT syntax (not portable):\nSELECT year, [1] AS q1, [2] AS q2, [3] AS q3, [4] AS q4\nFROM   quarterly_revenue\nPIVOT (SUM(revenue) FOR quarter IN ([1],[2],[3],[4])) AS p;\n```\n\n**Rule of thumb:** use `CASE`-based conditional aggregation for portability\nand readability. Use SQL Server's `PIVOT` syntax only in SQL Server-specific\ncode where dynamic pivot (unknown column count) is needed — even then, it\nrequires dynamic SQL to handle a variable number of pivot columns.\n",15,null,{"description":11},"SQL conditional and NULL function interview questions — CASE, COALESCE, NULLIF, IIF, GREATEST, LEAST, NVL, NULL handling pitfalls, and conditional aggregation across Postgres, MySQL, and SQL Server.","sql\u002Ffunctions\u002Fconditional-null-functions","Conditional & NULL Functions","Built-in Functions","functions","2026-06-20","dBvP1iGJ3yvGCVGzunS29UEwxdb3VGDQxovXGTlNlQQ",[96,100,103],{"subtopic":97,"path":98,"order":99},"String & Numeric Functions","\u002Fsql\u002Ffunctions\u002Fstring-numeric-functions",1,{"subtopic":101,"path":102,"order":12},"Date & Time Functions","\u002Fsql\u002Ffunctions\u002Fdate-functions",{"subtopic":90,"path":21,"order":20},{"path":105,"title":106},"\u002Fblog\u002Fsql-case-coalesce-null-handling","SQL CASE, COALESCE, and NULL Handling — Practical Patterns",1782244107493]