[{"data":1,"prerenderedAt":106},["ShallowReactive",2],{"qa-\u002Fsql\u002Ffunctions\u002Fstring-numeric-functions":3},{"page":4,"siblings":94,"blog":103},{"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":84,"related":85,"seo":86,"seoDescription":87,"stem":88,"subtopic":89,"topic":90,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fsql\u002Ffunctions\u002Fstring-numeric-functions.md","String Numeric Functions",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"easy","md","SQL","sql",{},true,1,"\u002Fsql\u002Ffunctions\u002Fstring-numeric-functions",[23,27,31,35,39,43,47,52,56,60,64,68,72,76,80],{"id":24,"difficulty":14,"q":25,"a":26},"concat","How do you concatenate strings in SQL?","SQL offers both a standard operator and functions for string concatenation.\nThe `||` operator is ANSI standard; `CONCAT()` is supported everywhere and\nhandles NULLs differently.\n\n```sql\n-- ANSI standard: || operator (Postgres, SQL Server 2012+, SQLite)\nSELECT first_name || ' ' || last_name AS full_name FROM users;\n\n-- NULL propagation: NULL || anything = NULL\nSELECT 'Hello' || NULL;  -- → NULL\n\n-- CONCAT() function: NULLs treated as empty strings\nSELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;\n-- If first_name IS NULL → ' Smith' (NULL coerced to '')\n\n-- MySQL also has CONCAT_WS (with separator — skips NULLs)\nSELECT CONCAT_WS(' ', first_name, middle_name, last_name) AS full_name;\n-- skips NULL middle_name without leaving a double space\n```\n\n**Rule of thumb:** use `CONCAT_WS` when joining fields that may be NULL\nand you do not want extra separators. Use `||` in Postgres for simple\nconcatenation; use `CONCAT()` in MySQL-compatible code.\n",{"id":28,"difficulty":14,"q":29,"a":30},"substring","How do you extract a part of a string?","`SUBSTRING` (or `SUBSTR`) extracts a portion of a string by position and\noptional length.\n\n```sql\n-- Standard SQL: SUBSTRING(string FROM start FOR length)\nSELECT SUBSTRING('Hello World' FROM 7 FOR 5);   -- → 'World'\n\n-- Shorthand (all databases):\nSELECT SUBSTRING('Hello World', 7, 5);           -- → 'World'\nSELECT SUBSTR('Hello World', 7, 5);              -- → 'World' (MySQL, Postgres)\n\n-- Extract from end: use negative position in MySQL\nSELECT SUBSTRING('Hello World', -5);             -- → 'World' (MySQL only)\n\n-- Postgres: RIGHT \u002F LEFT shortcuts\nSELECT LEFT('Hello World', 5);   -- → 'Hello'\nSELECT RIGHT('Hello World', 5);  -- → 'World'\n\n-- Regex-based extraction (Postgres)\nSELECT SUBSTRING('Order #12345' FROM '[0-9]+');  -- → '12345'\n```\n\n**Rule of thumb:** use `LEFT(str, n)` and `RIGHT(str, n)` for simple\nprefix\u002Fsuffix extraction — they are clearer than `SUBSTRING`. Use the\nregex form of `SUBSTRING` in Postgres for pattern-based extraction.\n",{"id":32,"difficulty":14,"q":33,"a":34},"upper-lower","How do you change string case in SQL?","`UPPER()` and `LOWER()` convert all characters in a string to upper or lower\ncase. `INITCAP()` (Postgres, Oracle) title-cases each word.\n\n```sql\nSELECT UPPER('hello world');    -- → 'HELLO WORLD'\nSELECT LOWER('HELLO WORLD');    -- → 'hello world'\nSELECT INITCAP('hello world');  -- → 'Hello World'  (Postgres\u002FOracle)\n\n-- Common use: case-insensitive comparison\nSELECT * FROM users WHERE LOWER(email) = 'alice@example.com';\n-- Better: create a functional index so this is fast:\n-- CREATE INDEX idx_users_email_lower ON users (LOWER(email));\n```\n\n**Rule of thumb:** for case-insensitive searches, use `LOWER()` with a\nmatching functional index rather than `ILIKE` (Postgres) on large tables,\nbecause `LOWER(col) = value` can be indexed but `col ILIKE value` typically\ncannot.\n",{"id":36,"difficulty":14,"q":37,"a":38},"trim","How do you remove whitespace or specific characters from a string?","`TRIM`, `LTRIM`, and `RTRIM` remove characters (default: spaces) from the\nstart, end, or both ends of a string.\n\n```sql\nSELECT TRIM('  hello  ');           -- → 'hello'\nSELECT LTRIM('  hello  ');          -- → 'hello  '\nSELECT RTRIM('  hello  ');          -- → '  hello'\n\n-- Remove specific characters (Postgres \u002F SQL Server)\nSELECT TRIM(BOTH '.' FROM '...hello...');   -- → 'hello'\nSELECT TRIM(LEADING '0' FROM '00042');       -- → '42'\n\n-- MySQL TRIM with specific characters\nSELECT TRIM(LEADING '0' FROM '00042');       -- → '42'\n```\n\n**Rule of thumb:** always `TRIM()` user-supplied input before storing it\nin the database. Trailing spaces cause subtle equality failures and bloat\n`CHAR(n)` columns.\n",{"id":40,"difficulty":14,"q":41,"a":42},"replace","How do you replace occurrences of a substring?","`REPLACE(source, from, to)` substitutes all occurrences of `from` with `to`\nin `source`. The replacement is case-sensitive in most databases.\n\n```sql\nSELECT REPLACE('Hello World', 'World', 'SQL');   -- → 'Hello SQL'\nSELECT REPLACE('aababab', 'ab', 'X');            -- → 'aXXX'\n\n-- Common use: sanitise data\nUPDATE products SET sku = REPLACE(sku, '-', '');  -- remove dashes from SKUs\n\n-- Postgres: regexp_replace for pattern-based replacement\nSELECT regexp_replace('Order #12345', '[0-9]+', 'XXXXXX');  -- → 'Order #XXXXXX'\nSELECT regexp_replace('a1b2c3', '[0-9]', '#', 'g');         -- → 'a#b#c#'\n-- 'g' flag = replace all occurrences (global)\n```\n\n**Rule of thumb:** use `REPLACE` for literal string swaps; use\n`regexp_replace` (Postgres) or `REGEXP_REPLACE` (MySQL 8+) when the\npattern to replace requires a regular expression.\n",{"id":44,"difficulty":14,"q":45,"a":46},"length","How do you get the length of a string in SQL?","`LENGTH` and `CHAR_LENGTH` return the number of characters in a string.\n`OCTET_LENGTH` returns the byte count (differs for multi-byte UTF-8).\n\n```sql\nSELECT LENGTH('Hello');          -- → 5\nSELECT CHAR_LENGTH('Hello');     -- → 5 (standard SQL; MySQL synonym)\nSELECT LENGTH('こんにちは');      -- Postgres: 5 chars; MySQL: 15 bytes!\n\n-- Postgres: char vs byte length\nSELECT char_length('こんにちは');  -- → 5 (characters)\nSELECT octet_length('こんにちは'); -- → 15 (bytes in UTF-8)\n\n-- Practical use: enforce a max length\nSELECT * FROM users WHERE LENGTH(username) > 50;\n```\n\n**Rule of thumb:** in MySQL, `LENGTH()` returns byte count for multibyte\nstrings — use `CHAR_LENGTH()` to count characters. In Postgres, `LENGTH()`\nreturns character count.\n",{"id":48,"difficulty":49,"q":50,"a":51},"string-search","medium","How do you search for a substring within a string?","`POSITION`, `CHARINDEX`, `INSTR`, and `STRPOS` all find the starting\nposition of a substring (returning 0 or NULL if not found, depending on\nthe database).\n\n```sql\n-- ANSI standard\nSELECT POSITION('World' IN 'Hello World');    -- → 7\n\n-- Postgres\nSELECT STRPOS('Hello World', 'World');         -- → 7\n\n-- MySQL\nSELECT INSTR('Hello World', 'World');          -- → 7\nSELECT LOCATE('World', 'Hello World');         -- → 7\nSELECT LOCATE('World', 'Hello World', 8);      -- → 0 (start search at pos 8)\n\n-- SQL Server\nSELECT CHARINDEX('World', 'Hello World');      -- → 7\n\n-- Check existence: returns 0 if not found\nSELECT * FROM products WHERE POSITION('PRO' IN UPPER(sku)) > 0;\n-- Often better expressed as:\nSELECT * FROM products WHERE sku ILIKE '%PRO%';  -- Postgres\n```\n\n**Rule of thumb:** use `LIKE` or `ILIKE` for simple contains-checks — they\nare cleaner and the optimizer understands them. Use position functions when\nyou need the actual offset or want to split on a delimiter.\n",{"id":53,"difficulty":14,"q":54,"a":55},"round-ceil-floor","How do you round numeric values in SQL?","`ROUND`, `CEIL`\u002F`CEILING`, and `FLOOR` control numeric rounding.\n\n```sql\nSELECT ROUND(3.14159, 2);    -- → 3.14\nSELECT ROUND(3.145, 2);      -- → 3.15 (or 3.14 — depends on type\u002Frounding mode)\nSELECT ROUND(3.5);           -- → 4\nSELECT ROUND(-3.5);          -- → -4 (Postgres); -3 (some databases)\n\nSELECT CEIL(3.2);            -- → 4  (round up to nearest integer)\nSELECT CEILING(3.2);         -- → 4  (SQL Server alias)\nSELECT FLOOR(3.8);           -- → 3  (round down to nearest integer)\n\n-- Truncate without rounding (Postgres)\nSELECT TRUNC(3.999, 1);      -- → 3.9\nSELECT TRUNC(-3.999, 1);     -- → -3.9 (towards zero, not floor)\n\n-- Practical: round to 2 decimal places for currency display\nSELECT ROUND(SUM(total), 2) AS revenue FROM orders;\n```\n\n**Rule of thumb:** use `ROUND(x, 2)` for display formatting of monetary\nvalues. Use `TRUNC` (not `FLOOR`) when you need to drop fractional parts\nwithout rounding, especially for negative numbers.\n",{"id":57,"difficulty":14,"q":58,"a":59},"mod-abs-power","What are MOD, ABS, and POWER and how do you use them?","```sql\n-- MOD: remainder after integer division\nSELECT MOD(10, 3);      -- → 1\nSELECT 10 % 3;          -- → 1 (Postgres, SQL Server, MySQL)\n-- Common use: every Nth row, parity checks\nSELECT id FROM orders WHERE MOD(id, 2) = 0;  -- even IDs\n\n-- ABS: absolute value (removes sign)\nSELECT ABS(-42);        -- → 42\nSELECT ABS(-3.14);      -- → 3.14\n-- Use: distance calculations, deviation from target\nSELECT product_id, ABS(actual_stock - expected_stock) AS discrepancy\nFROM inventory_check;\n\n-- POWER: exponentiation\nSELECT POWER(2, 10);    -- → 1024\nSELECT 2 ^ 10;          -- → 1024 (Postgres)\n-- Use: compound interest, exponential growth models\nSELECT principal * POWER(1 + rate, years) AS future_value\nFROM loans;\n```\n\n**Rule of thumb:** prefer the operator form (`%`, `^`) in Postgres for\nbrevity; use `MOD()` and `POWER()` for cross-database portability.\n",{"id":61,"difficulty":49,"q":62,"a":63},"cast-convert","How do you convert a value from one data type to another?","Type conversion is done with `CAST` (standard), `::` (Postgres shorthand),\nor `CONVERT` (MySQL\u002FSQL Server).\n\n```sql\n-- Standard SQL CAST\nSELECT CAST('42' AS INTEGER);              -- string → integer\nSELECT CAST(3.14 AS TEXT);                 -- number → string\nSELECT CAST('2026-06-20' AS DATE);         -- string → date\n\n-- Postgres shorthand\nSELECT '42'::INTEGER;\nSELECT 3.14::TEXT;\nSELECT '2026-06-20'::DATE;\n\n-- MySQL CONVERT\nSELECT CONVERT('42', UNSIGNED);\nSELECT CONVERT(3.14, CHAR);\n\n-- SQL Server CONVERT (also supports format style codes)\nSELECT CONVERT(INT, '42');\nSELECT CONVERT(VARCHAR, GETDATE(), 103);   -- → '20\u002F06\u002F2026' (UK format)\n\n-- Safe cast (returns NULL instead of error on failure — Postgres 14+)\nSELECT pg_catalog.pg_safe_cast('abc', 'integer');  -- → NULL, no error\n-- MySQL: CAST('abc' AS UNSIGNED) → 0 (silent coercion, not NULL)\n```\n\n**Rule of thumb:** use `CAST(x AS type)` for portability. In Postgres,\n`::type` is idiomatic. Always validate input before casting to avoid\nruntime errors; use `TRY_CAST` (SQL Server) or `TRY_CONVERT` to return\nNULL on failure instead of raising an exception.\n",{"id":65,"difficulty":14,"q":66,"a":67},"string-padding","How do you pad a string to a fixed length?","`LPAD` and `RPAD` pad a string to a target length with a specified fill\ncharacter (defaulting to spaces).\n\n```sql\nSELECT LPAD('42', 5, '0');     -- → '00042'  (zero-pad a number)\nSELECT RPAD('hello', 8, '.');  -- → 'hello...'\nSELECT LPAD('hello', 3, ' ');  -- → 'hel'  (truncates if longer!)\n\n-- Common use: format fixed-width codes\nSELECT LPAD(CAST(id AS TEXT), 8, '0') AS padded_id FROM orders;\n-- id=42 → '00000042'\n\n-- Postgres alternative for numeric zero-padding\nSELECT TO_CHAR(42, 'FM00000000');  -- → '00000042'\n```\n\n**Rule of thumb:** use `LPAD(value::text, width, '0')` for zero-padding\nintegers when formatting export files or codes. Note that `LPAD` truncates\nfrom the left if the string is already longer than the target — always\nconfirm the max width before using it.\n",{"id":69,"difficulty":49,"q":70,"a":71},"format-to-char","How do you format numbers and dates as strings?","`TO_CHAR` (Postgres, Oracle) and `FORMAT` (MySQL, SQL Server) convert\nnumbers and dates to formatted strings.\n\n```sql\n-- Postgres TO_CHAR for numbers\nSELECT TO_CHAR(1234567.89, 'FM$999,999,990.00');  -- → '$1,234,567.89'\nSELECT TO_CHAR(0.153, 'FM90.0%');                  -- → '15.3%'\n\n-- Postgres TO_CHAR for dates\nSELECT TO_CHAR(now(), 'YYYY-MM-DD HH24:MI:SS');   -- → '2026-06-20 14:30:00'\nSELECT TO_CHAR(now(), 'Day, DD Month YYYY');       -- → 'Saturday, 20 June 2026'\n\n-- MySQL FORMAT for numbers\nSELECT FORMAT(1234567.89, 2);   -- → '1,234,567.89' (locale-aware)\n\n-- SQL Server FORMAT\nSELECT FORMAT(1234567.89, 'N2');            -- → '1,234,567.89'\nSELECT FORMAT(GETDATE(), 'yyyy-MM-dd');     -- → '2026-06-20'\n```\n\n**Rule of thumb:** format values for display in the application layer when\npossible — SQL formatting functions are less testable and locale-handling\nvaries. Use `TO_CHAR` in SQL when formatting is needed in the query itself\n(e.g., CSV exports, stored reports).\n",{"id":73,"difficulty":49,"q":74,"a":75},"string-split","How do you split a delimited string in SQL?","Splitting a delimited string (e.g., `'a,b,c'`) into rows requires\ndatabase-specific functions.\n\n```sql\n-- Postgres: STRING_TO_ARRAY + UNNEST\nSELECT UNNEST(STRING_TO_ARRAY('a,b,c', ',')) AS item;\n-- → rows: 'a', 'b', 'c'\n\n-- Postgres: regexp_split_to_table\nSELECT regexp_split_to_table('one two three', '\\s+') AS word;\n\n-- MySQL 8+: JSON_TABLE workaround (no native split)\nSELECT jt.item\nFROM JSON_TABLE(\n  CONCAT('[\"', REPLACE('a,b,c', ',', '\",\"'), '\"]'),\n  '$[*]' COLUMNS (item VARCHAR(100) PATH '$')\n) AS jt;\n\n-- SQL Server: STRING_SPLIT (SQL Server 2016+)\nSELECT value AS item FROM STRING_SPLIT('a,b,c', ',');\n```\n\n**Rule of thumb:** storing comma-separated values in a single column is a\n1NF violation — use a child table instead. When you must parse a legacy\nstring, `UNNEST(STRING_TO_ARRAY(...))` in Postgres is the cleanest approach.\n",{"id":77,"difficulty":49,"q":78,"a":79},"aggregate-string","How do you aggregate multiple rows into a single string?","`STRING_AGG` (Postgres 9.0+, SQL Server 2017+) and `GROUP_CONCAT` (MySQL)\nconcatenate values from multiple rows into one string, with a separator.\n\n```sql\n-- Postgres \u002F SQL Server\nSELECT customer_id,\n       STRING_AGG(product_name, ', ' ORDER BY product_name) AS products\nFROM   order_items\nGROUP  BY customer_id;\n-- → customer_id=1: 'Pen, Pencil, Ruler'\n\n-- MySQL\nSELECT customer_id,\n       GROUP_CONCAT(product_name ORDER BY product_name SEPARATOR ', ') AS products\nFROM   order_items\nGROUP  BY customer_id;\n\n-- With DISTINCT to deduplicate\nSELECT STRING_AGG(DISTINCT tag, ', ') FROM article_tags WHERE article_id = 1;\n```\n\n**Rule of thumb:** use `STRING_AGG` to build comma-separated lists for\nreports or JSON responses without a second round-trip. Be mindful of the\nresult length — `GROUP_CONCAT` in MySQL defaults to a 1 024-byte limit\n(configurable via `group_concat_max_len`).\n",{"id":81,"difficulty":49,"q":82,"a":83},"regex-functions","How do you use regular expressions in SQL?","Most databases support regex-based filtering and extraction, though the\nsyntax differs.\n\n```sql\n-- Postgres: ~ (match), !~ (no match), ~* (case-insensitive)\nSELECT * FROM users WHERE email ~ '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$';\n-- Extract a substring matching a pattern\nSELECT REGEXP_MATCH(description, '\\d+') AS first_number FROM products;\n-- Replace with regex\nSELECT REGEXP_REPLACE(phone, '[^0-9]', '', 'g') AS digits_only FROM users;\n\n-- MySQL: REGEXP \u002F RLIKE (filter), REGEXP_REPLACE, REGEXP_SUBSTR (8.0+)\nSELECT * FROM users WHERE email REGEXP '^[a-z0-9._%+-]+@';\nSELECT REGEXP_REPLACE(phone, '[^0-9]', '') FROM users;\n\n-- SQL Server: no built-in regex; use LIKE for simple patterns\n-- or CLR functions \u002F JSON PATH for complex cases\nSELECT * FROM users WHERE email LIKE '%@%.%';\n```\n\n**Rule of thumb:** use `LIKE` for simple prefix\u002Fsuffix\u002Fcontains patterns —\nit is portable and index-friendly (leading wildcards aside). Use regex\nfunctions only when the pattern is too complex for `LIKE`, and only in\nPostgres or MySQL where support is solid.\n",15,null,{"description":11},"SQL string and numeric function interview questions — CONCAT, SUBSTRING, TRIM, REPLACE, UPPER\u002FLOWER, LENGTH, ROUND, CEIL, FLOOR, MOD, CAST, and dialect differences across Postgres, MySQL, and SQL Server.","sql\u002Ffunctions\u002Fstring-numeric-functions","String & Numeric Functions","Built-in Functions","functions","2026-06-20","jLD6OehmtGFFohDiH_eWEI5-sQoDyYQ_QrEhbvk3apg",[95,96,99],{"subtopic":89,"path":21,"order":20},{"subtopic":97,"path":98,"order":12},"Date & Time Functions","\u002Fsql\u002Ffunctions\u002Fdate-functions",{"subtopic":100,"path":101,"order":102},"Conditional & NULL Functions","\u002Fsql\u002Ffunctions\u002Fconditional-null-functions",3,{"path":104,"title":105},"\u002Fblog\u002Fsql-string-numeric-functions","SQL String & Numeric Functions — CONCAT, SUBSTRING, ROUND, and More",1782244107420]