# Text functions
> Change case, cut, trim, pad, search and join text. A letter of any language (Nepali, Chinese, ...) counts as one character.
- Category: Functions — text
- Keywords: text, string, STRING, ascii, chr, translate, overlay, PLACING, normalize, NFC, NFD, NFKC, NFKD, U&, to\_ascii, remove accents, bit\_length, to\_hex, parse\_ident, string\_to\_table, number format, format a number, VARCHAR, UTF-8, Unicode, Nepali, Devanagari, characters, letters, bytes, upper case, lower case, cut text, clean text, ||, join text, concatenate, lcase, ucase, len, mid, instr, locate, position IN, lengthb, format, sprintf, printf, string format, quote a name, quote a value, quote\_ident, quote\_literal, quote\_nullable, SQL injection
- See also: [Regular expressions](functions-regex.md), [WHERE](where.md), [Conversion functions](functions-conversion.md), [Bytes, hex and base64](functions-bytes.md), [Full-text search](full-text-search.md)
These functions work on text (`STRING`). KDB keeps text as **UTF-8**. UTF-8 is the usual way to store the letters of every language. An English letter takes 1 byte; a Nepali letter like `न` takes 3 bytes.
- Text functions count **characters**, not bytes. `length('नेपाल')` is `5`. Only `octet_length` counts bytes.
- A `NULL` argument gives `NULL`. Only `concat`, `concat_ws`, `format` and `quote_nullable` take NULLs.
- A number, date or `true`/`false` is first turned into text: `length(12.50)` is `4`, because the text is `12.5`.
- `upper`, `lower` and `initcap` change the letters of every script that has capitals: English, `Ä`, `ě`, Vietnamese, Greek, Cyrillic, Armenian and more. Nepali (Devanagari) has no capitals, so it stays as it is.
- Searching (`replace`, `strpos`, `starts_with`, ...) cares about upper and lower case. To ignore case, use `lower()` on both sides, [ILIKE](where.md) or a [regular expression](functions-regex.md) with the flag `'i'`.
- To find text with a pattern, use [LIKE and ILIKE](where.md) or the [regular expression functions](functions-regex.md).
**Example: Clean labels for the company list** (sample data: sample)
```sql
SELECT symbol,
lower(symbol) AS url_part,
split_part(name, ' ', 1) AS first_word,
length(name) AS letters,
symbol || ' - ' || left(name, 20) AS label
FROM securities
ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | url_part (STRING) | first_word (STRING) | letters (LONG) | label (STRING) |
|---|---|---|---|---|
| ACME | acme | Acme | 23 | ACME - Acme Investment Comp |
| CITY | city | City | 17 | CITY - City Bank Limited |
| COAST | coast | Coastal | 20 | COAST - Coastal Bank Limited |
| TELCO | telco | Northwind | 25 | TELCO - Northwind Telecom Li |
| VOLT | volt | Volt | 29 | VOLT - Volt River Hydropowe |
(5 rows)
## lower
Syntax:
```sql
lower(text)
```
The text with every capital letter made small: `lower('CITY')` is `city`, `lower('ÄÖ')` is `äö`, `lower('ΣΑΣ')` is `σασ`. Letters without capitals (Nepali) stay as they are.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (a number is turned into text) | the text to change |
- **Returns:** `STRING`.
- **NULL:** `lower(NULL)` is `NULL`.
- **Notes:** each letter becomes one letter, as in PostgreSQL: `lower('İ')` is `i`.
**Example**
```sql
SELECT lower('CITY Bank') AS a, lower('नेपाल') AS nepali, lower('ÄBC') AS german, lower('ПРИВЕТ') AS russian, lower(NULL) AS b;
```
Result:
| a (STRING) | nepali (STRING) | german (STRING) | russian (STRING) | b (STRING) |
|---|---|---|---|---|
| city bank | नेपाल | äbc | привет | NULL |
(1 row)
**Example: Log in with the user name in any case**
Setup:
```sql
CREATE TABLE app_users (id INT PRIMARY KEY AUTO, username STRING NOT NULL, email STRING);
INSERT INTO app_users (username, email) VALUES ('Ram_Sharma', 'ram@example.com'), ('sita', 'sita@example.com');
```
```sql
SELECT id, username FROM app_users WHERE lower(username) = lower('RAM_sharma');
```
Result:
| id (INT) | username (STRING) |
|---|---|
| 1 | Ram_Sharma |
(1 row)
## upper
Syntax:
```sql
upper(text)
```
The text with every small letter made capital: `upper('city')` is `CITY`, `upper('ěšč')` is `ĚŠČ`. Letters without capitals (Nepali) stay as they are.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (a number is turned into text) | the text to change |
- **Returns:** `STRING`.
- **NULL:** `upper(NULL)` is `NULL`.
- **Notes:** `upper('ß')` stays `ß`, as in PostgreSQL (one letter becomes one letter).
**Example**
```sql
SELECT upper('city नेपाल') AS a, upper('Coast') AS b, upper('crème brûlée') AS french, upper(NULL) AS c;
```
Result:
| a (STRING) | b (STRING) | french (STRING) | c (STRING) |
|---|---|---|---|
| CITY नेपाल | COAST | CRÈME BRÛLÉE | NULL |
(1 row)
**Example: Clean stock symbols typed by users**
Setup:
```sql
CREATE TABLE watchlist (id INT PRIMARY KEY AUTO, typed STRING);
INSERT INTO watchlist (typed) VALUES (' city '), ('Coast'), ('volt ');
```
```sql
SELECT id, '[' || typed || ']' AS typed, upper(trim(typed)) AS symbol FROM watchlist ORDER BY id;
```
Result:
| id (INT) | typed (STRING) | symbol (STRING) |
|---|---|---|
| 1 | [ city ] | CITY |
| 2 | [Coast] | COAST |
| 3 | [volt ] | VOLT |
(3 rows)
## length, char_length, character_length
Syntax:
```sql
length(text)
char_length(text)
character_length(text)
```
The number of characters in the text. A Nepali letter is one character. The three names are the same function.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING, BYTES, or a value turned into text | the value to measure |
- **Returns:** `LONG`.
- **NULL:** `length(NULL)` is `NULL`.
- **Notes:** on a `BYTES` value it counts bytes (see [bytes functions](functions-bytes.md)). Some Nepali letters are made of 2 characters: `के` is `क` plus the sign `े`, so `length('के')` is `2`.
**Example**
```sql
SELECT length('CITY') AS a, length('नेपाल') AS nepali, length('के') AS ke, char_length('') AS empty, character_length('City Bank') AS b, length(12.50) AS number, length(NULL) AS c;
```
Result:
| a (LONG) | nepali (LONG) | ke (LONG) | empty (LONG) | b (LONG) | number (LONG) | c (STRING) |
|---|---|---|---|---|---|---|
| 4 | 5 | 2 | 0 | 9 | 4 | NULL |
(1 row)
**Example: Refuse a forum post that is too short or too long**
```sql
SELECT body, length(body) AS letters, length(body) BETWEEN 10 AND 500 AS ok
FROM (SELECT 'Good results!' AS body
UNION ALL SELECT 'नाफा राम्रो छ'
UNION ALL SELECT 'ok') AS posts;
```
Result:
| body (STRING) | letters (LONG) | ok (BOOL) |
|---|---|---|
| Good results! | 13 | true |
| नाफा राम्रो छ | 13 | true |
| ok | 2 | false |
(3 rows)
**Example: Company names in English and Nepali**
Setup:
```sql
CREATE TABLE companies (symbol STRING PRIMARY KEY, name STRING, name_ne STRING);
INSERT INTO companies VALUES ('CITY', 'City Bank Limited', 'सिटी बैंक लिमिटेड'), ('VOLT', 'Volt River Hydropower Limited', 'भोल्ट रिभर जलविद्युत लिमिटेड');
```
```sql
SELECT symbol, length(name) AS name_letters, name_ne, length(name_ne) AS ne_letters FROM companies ORDER BY symbol;
```
Result:
| symbol (STRING) | name_letters (LONG) | name_ne (STRING) | ne_letters (LONG) |
|---|---|---|---|
| CITY | 17 | सिटी बैंक लिमिटेड | 17 |
| VOLT | 29 | भोल्ट रिभर जलविद्युत लिमिटेड | 28 |
(2 rows)
## octet_length
Syntax:
```sql
octet_length(text)
```
The number of **bytes** the text takes in UTF-8. An English letter is 1 byte; a Nepali letter is 3 bytes.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING, BYTES, or a value turned into text | the value to measure |
- **Returns:** `LONG`.
- **NULL:** `octet_length(NULL)` is `NULL`.
- **Notes:** use it when a limit is in bytes (for example a column in another program that takes at most 255 bytes). For letters people see, use `length`.
**Example**
```sql
SELECT length('नेपाल') AS letters, octet_length('नेपाल') AS bytes, octet_length('CITY') AS english_bytes, octet_length('') AS empty;
```
Result:
| letters (LONG) | bytes (LONG) | english_bytes (LONG) | empty (LONG) |
|---|---|---|---|
| 5 | 15 | 4 | 0 |
(1 row)
## substr, substring
Syntax:
```sql
substr(text, start)
substr(text, start, count)
substring(text, start [, count])
```
A part of the text: `count` characters from character number `start`. The first character is number 1. Without `count`, you get everything to the end. `substring` is the same function.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (or BYTES) | the text to cut |
| `start` | whole number | the first character to keep; `1` = first. `0` or less starts before the text, so fewer characters are left |
| `count` | whole number | how many characters; must not be negative |
- **Returns:** `STRING` (`BYTES` for a `BYTES` value, counted in bytes).
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** `start` after the end gives empty text `''`, not an error. A negative `count` is an error. `start` and `count` must be whole numbers: `'2'` or `2.5` is an error. The PostgreSQL form `substring(text FROM 2 FOR 3)` works too, and means the same as `substring(text, 2, 3)`.
**Example**
```sql
SELECT substr('CITY', 3) AS a, substring('CITY', 2, 3) AS b, substr('नेपाल', 2, 2) AS nepali, substr('CITY', 0, 3) AS from_zero, substr('CITY', 10) AS after_end;
```
Result:
| a (STRING) | b (STRING) | nepali (STRING) | from_zero (STRING) | after_end (STRING) |
|---|---|---|---|---|
| TY | ITY | ेप | CI | |
(1 row)
**Example: Nepali fiscal year 2082/83: first and last year**
```sql
SELECT substr('2082/83', 1, 4) AS first_year, substr('2082/83', 6) AS second_year;
```
Result:
| first_year (STRING) | second_year (STRING) |
|---|---|
| 2082 | 83 |
(1 row)
**Example: A negative count is an error** (shows an error)
```sql
SELECT substr('CITY', 2, -1);
```
Result:
Error: `line 1, column 8: substr: the length must not be negative, got -1`
**Example: The PostgreSQL FROM ... FOR form**
```sql
SELECT substring('CITY' FROM 2 FOR 3) AS from_for, substring('CITY' FROM 2) AS to_the_end;
```
Result:
| from_for (STRING) | to_the_end (STRING) |
|---|---|
| ITY | ITY |
(1 row)
## left
Syntax:
```sql
left(text, n)
```
The first `n` characters of the text. With a negative `n`, everything except the last `-n` characters.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text |
| `n` | whole number | how many characters to keep from the start; negative = how many to drop from the end |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** it never cuts a Nepali letter in half. `n` bigger than the text gives the whole text.
**Example**
```sql
SELECT left('CITY', 3) AS a, left('CITY', -2) AS drop_last_2, left('नेपाल बैंक', 5) AS nepali, left('CITY', 99) AS whole;
```
Result:
| a (STRING) | drop_last_2 (STRING) | nepali (STRING) | whole (STRING) |
|---|---|---|---|
| CIT | CI | नेपाल | CITY |
(1 row)
**Example: Short preview of long company names** (sample data: sample)
```sql
SELECT symbol,
CASE WHEN length(name) > 25 THEN left(name, 25) || '...' ELSE name END AS preview
FROM securities
ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | preview (STRING) |
|---|---|
| ACME | Acme Investment Company |
| CITY | City Bank Limited |
| COAST | Coastal Bank Limited |
| TELCO | Northwind Telecom Limited |
| VOLT | Volt River Hydropower Lim... |
(5 rows)
## right
Syntax:
```sql
right(text, n)
```
The last `n` characters of the text. With a negative `n`, everything except the first `-n` characters.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text |
| `n` | whole number | how many characters to keep from the end; negative = how many to drop from the start |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
**Example**
```sql
SELECT right('CITY', 3) AS a, right('CITY', -2) AS drop_first_2, right('नेपाल', 2) AS nepali;
```
Result:
| a (STRING) | drop_first_2 (STRING) | nepali (STRING) |
|---|---|---|
| ITY | TY | ाल |
(1 row)
**Example: Show only the last 4 digits of a phone number**
```sql
SELECT '******' || right('9841234567', 4) AS phone_shown;
```
Result:
| phone_shown (STRING) |
|---|
| ******4567 |
(1 row)
## trim, btrim
Syntax:
```sql
trim(text)
trim(text, characters)
btrim(text [, characters])
```
Removes spaces from both ends of the text. With `characters`, it removes any of those characters from both ends instead. `btrim` is the same function.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to clean |
| `characters` | STRING | the characters to remove, in any order; default: space, tab, carriage return and line break |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** `characters` is a set of single characters, not a word: `trim('xyCITYyx', 'xy')` removes every `x` and `y` at the ends. Characters in the middle stay. The standard forms `trim(BOTH 'x' FROM text)`, `trim(LEADING 'x' FROM text)` and `trim(TRAILING 'x' FROM text)` work too; `LEADING` is [ltrim](#ltrim) and `TRAILING` is [rtrim](#rtrim).
**Example**
```sql
SELECT '[' || trim(' CITY ') || ']' AS a, trim('xxCOASTxx', 'x') AS b, btrim('--a-b--', '-') AS c, trim('xyCITYyx', 'xy') AS d;
```
Result:
| a (STRING) | b (STRING) | c (STRING) | d (STRING) |
|---|---|---|---|
| [CITY] | COAST | a-b | CITY |
(1 row)
**Example: Clean a user name before saving it**
Setup:
```sql
CREATE TABLE app_users (id INT PRIMARY KEY AUTO, username STRING NOT NULL);
```
```sql
INSERT INTO app_users (username) VALUES (lower(trim(' Hari_KC ')));
SELECT id, '[' || username || ']' AS saved FROM app_users;
```
Result:
`INSERT INTO app_users (username) VALUES (lower(trim(' Hari_KC ')))`
```text
1 row(s) inserted
```
`SELECT id, '[' || username || ']' AS saved FROM app_users`
| id (INT) | saved (STRING) |
|---|---|
| 1 | [hari_kc] |
(1 row)
**Example: The standard BOTH / LEADING / TRAILING forms**
```sql
SELECT trim(BOTH 'x' FROM 'xxCOASTxx') AS both_ends,
trim(LEADING 'x' FROM 'xxCOASTxx') AS front_only,
trim(TRAILING 'x' FROM 'xxCOASTxx') AS end_only;
```
Result:
| both_ends (STRING) | front_only (STRING) | end_only (STRING) |
|---|---|---|
| COAST | COASTxx | xxCOAST |
(1 row)
## ltrim
Syntax:
```sql
ltrim(text [, characters])
```
Like `trim`, but only at the **start** (left side) of the text.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to clean |
| `characters` | STRING | the characters to remove; default: space, tab, carriage return and line break |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
**Example**
```sql
SELECT '[' || ltrim(' CITY ') || ']' AS a, ltrim('00042', '0') AS no_zeros;
```
Result:
| a (STRING) | no_zeros (STRING) |
|---|---|
| [CITY ] | 42 |
(1 row)
## rtrim
Syntax:
```sql
rtrim(text [, characters])
```
Like `trim`, but only at the **end** (right side) of the text.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to clean |
| `characters` | STRING | the characters to remove; default: space, tab, carriage return and line break |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
**Example**
```sql
SELECT '[' || rtrim(' CITY ') || ']' AS a, rtrim('नेपाल।।', '।') AS nepali_full_stop, rtrim('512.500', '0') AS price;
```
Result:
| a (STRING) | nepali_full_stop (STRING) | price (STRING) |
|---|---|---|
| [ CITY] | नेपाल | 512.5 |
(1 row)
## replace
Syntax:
```sql
replace(text, from, to)
```
Replaces **every** place where `from` is found in `text` with `to`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text |
| `from` | STRING | the text to find (upper and lower case must match) |
| `to` | STRING | the new text; `''` removes `from` |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** an empty `from` changes nothing. The search does not ignore case: `replace('City', 'city', 'x')` is `City`. For patterns (any digit, any space) use [regexp_replace](functions-regex.md#regexp-replace).
**Example**
```sql
SELECT replace('a-b-c', '-', '/') AS a, replace('City Bank', 'Bank', 'Bank Limited') AS b, replace('City', 'city', 'x') AS case_matters, replace('CITY', '', 'x') AS empty_from;
```
Result:
| a (STRING) | b (STRING) | case_matters (STRING) | empty_from (STRING) |
|---|---|---|---|
| a/b/c | City Bank Limited | City | CITY |
(1 row)
**Example: Turn a price with commas into a number**
```sql
SELECT CAST(replace('1,234,567.50', ',', '') AS DOUBLE) AS amount;
```
Result:
| amount (DOUBLE) |
|---|
| 1234567.5 |
(1 row)
## split_part
Syntax:
```sql
split_part(text, separator, n)
```
Splits the text at every `separator` and gives part number `n`. `1` is the first part, `-1` is the last part.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to split |
| `separator` | STRING | the text between the parts (one or more characters) |
| `n` | whole number | the part: `1` first, `2` second, `-1` last, `-2` second last. `0` is an error |
- **Returns:** `STRING`. A part that does not exist gives empty text `''`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** an empty `separator` gives the whole text as part 1. PostgreSQL also allows negative `n` (since version 14); MySQL has no `split_part`.
**Example**
```sql
SELECT split_part('2082/83', '/', 1) AS a, split_part('2082/83', '/', -1) AS last, split_part('a,b', ',', 5) AS missing;
```
Result:
| a (STRING) | last (STRING) | missing (STRING) |
|---|---|---|
| 2082 | 83 | |
(1 row)
**Example: The domain of each user's email**
Setup:
```sql
CREATE TABLE app_users (id INT PRIMARY KEY AUTO, email STRING NOT NULL);
INSERT INTO app_users (email) VALUES ('ram@example.com'), ('sita@mail.com.np'), ('hari@example.com');
```
```sql
SELECT split_part(email, '@', 2) AS domain, count(*) AS users FROM app_users GROUP BY split_part(email, '@', 2) ORDER BY domain;
```
Result:
| domain (STRING) | users (LONG) |
|---|---|
| example.com | 2 |
| mail.com.np | 1 |
(2 rows)
**Example: Part number 0 does not exist** (shows an error)
```sql
SELECT split_part('2082/83', '/', 0);
```
Result:
Error: `line 1, column 8: split_part: the field number must not be 0 (1 = first, -1 = last)`
## strpos
Syntax:
```sql
strpos(text, part)
```
Where `part` first starts in `text`, as a character number (1 = first). `0` when it is not there.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to search in |
| `part` | STRING | the text to find (case must match) |
- **Returns:** `LONG`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** the number counts characters, so it works with Nepali text. An empty `part` gives `1`. `position(part IN text)` does the same with the arguments the other way round.
**Example**
```sql
SELECT strpos('CITY', 'IT') AS a, strpos('नेपाल बैंक', 'बैंक') AS nepali, strpos('CITY', 'x') AS not_found, strpos('City', 'cit') AS case_matters;
```
Result:
| a (LONG) | nepali (LONG) | not_found (LONG) | case_matters (LONG) |
|---|---|---|---|
| 2 | 7 | 0 | 0 |
(1 row)
**Example: The user name before the @ of an email**
```sql
SELECT email, left(email, strpos(email, '@') - 1) AS name
FROM (SELECT 'ram.sharma@example.com' AS email UNION ALL SELECT 'sita@mail.com.np') AS e;
```
Result:
| email (STRING) | name (STRING) |
|---|---|
| ram.sharma@example.com | ram.sharma |
| sita@mail.com.np | sita |
(2 rows)
## position
Syntax:
```sql
position(part IN text)
position(part, text)
```
Where `part` first starts in `text`, as a character number (1 = first). `0` when it is not there. The same as `strpos(text, part)`.
| Argument | Type | Meaning |
|---|---|---|
| `part` | STRING | the text to find (case must match) |
| `text` | STRING | the text to search in |
- **Returns:** `LONG`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** `part` comes **first** here, but second in `strpos`.
**Example**
```sql
SELECT position('बैंक' IN 'नेपाल बैंक') AS nepali, position('TY' IN 'CITY') AS a, position('TY', 'CITY') AS b, position('x' IN 'CITY') AS not_found;
```
Result:
| nepali (LONG) | a (LONG) | b (LONG) | not_found (LONG) |
|---|---|---|---|
| 7 | 3 | 3 | 0 |
(1 row)
## starts_with
Syntax:
```sql
starts_with(text, prefix)
```
`true` when the text begins with `prefix`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text |
| `prefix` | STRING | the start to look for (case must match) |
- **Returns:** `BOOL`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** an empty `prefix` is always `true`. `text LIKE 'NA%'` gives the same answer, and it can use an [index](indexes.md).
**Example**
```sql
SELECT starts_with('CITY', 'CI') AS a, starts_with('city', 'CI') AS case_matters, starts_with('CITY', '') AS empty;
```
Result:
| a (BOOL) | case_matters (BOOL) | empty (BOOL) |
|---|---|---|
| true | false | true |
(1 row)
**Example: Companies whose symbol starts with N** (sample data: sample)
```sql
SELECT symbol, name FROM securities WHERE starts_with(symbol, 'N') ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | name (STRING) |
|---|---|
(0 rows)
## ends_with
Syntax:
```sql
ends_with(text, suffix)
```
`true` when the text ends with `suffix`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text |
| `suffix` | STRING | the end to look for (case must match) |
- **Returns:** `BOOL`.
- **NULL:** `NULL` when any argument is `NULL`.
**Example**
```sql
SELECT ends_with('CITY', 'TY') AS a, ends_with('CITY', 'ty') AS case_matters;
```
Result:
| a (BOOL) | case_matters (BOOL) |
|---|---|
| true | false |
(1 row)
**Example: Users with an email address in Nepal (.np)**
Setup:
```sql
CREATE TABLE app_users (id INT PRIMARY KEY AUTO, email STRING NOT NULL);
INSERT INTO app_users (email) VALUES ('ram@example.com'), ('sita@mail.com.np'), ('hari@northwind.example.com');
```
```sql
SELECT id, email FROM app_users WHERE ends_with(lower(email), '.np') ORDER BY id;
```
Result:
| id (INT) | email (STRING) |
|---|---|
| 2 | sita@mail.com.np |
(1 row)
## lpad
Syntax:
```sql
lpad(text, length)
lpad(text, length, fill)
```
Makes the text `length` characters long by adding `fill` on the **left**. When the text is longer, it is cut to `length`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (a number is turned into text) | the text |
| `length` | whole number | the length you want, in characters (at most 10 000 000) |
| `fill` | STRING | what to add, repeated as needed; default one space |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** `length` of 0 or less gives `''`. An empty `fill` gives the text unchanged. A text that is too long is cut at the **end** (like PostgreSQL).
**Example**
```sql
SELECT lpad('7', 3, '0') AS a, '[' || lpad('5', 3) || ']' AS spaces, lpad('ab', 7, 'xy') AS b, lpad('CITY', 3) AS cut, lpad('न', 3, 'क') AS nepali;
```
Result:
| a (STRING) | spaces (STRING) | b (STRING) | cut (STRING) | nepali (STRING) |
|---|---|---|---|---|
| 007 | [ 5] | xyxyxab | CIT | ककन |
(1 row)
**Example: Invoice numbers with leading zeros**
Setup:
```sql
CREATE TABLE invoices (id INT PRIMARY KEY AUTO, amount DECIMAL(2));
INSERT INTO invoices (amount) VALUES (1500.00), (250.50), (99999.99);
```
```sql
SELECT 'INV-2082-' || lpad(id, 5, '0') AS invoice_no, amount FROM invoices ORDER BY id;
```
Result:
| invoice_no (STRING) | amount (DECIMAL(2)) |
|---|---|
| INV-2082-00001 | 1500.00 |
| INV-2082-00002 | 250.50 |
| INV-2082-00003 | 99999.99 |
(3 rows)
**Example: The length has a limit** (shows an error)
```sql
SELECT lpad('a', 20000000);
```
Result:
Error: `line 1, column 8: lpad: the length is larger than 10000000`
## rpad
Syntax:
```sql
rpad(text, length)
rpad(text, length, fill)
```
Makes the text `length` characters long by adding `fill` on the **right**. When the text is longer, it is cut to `length`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (a number is turned into text) | the text |
| `length` | whole number | the length you want, in characters (at most 10 000 000) |
| `fill` | STRING | what to add, repeated as needed; default one space |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** the same rules as `lpad`.
**Example**
```sql
SELECT rpad('CITY', 8, '.') AS a, rpad('CITY', 3) AS cut, '[' || rpad('COAST', 6) || ']' AS spaces;
```
Result:
| a (STRING) | cut (STRING) | spaces (STRING) |
|---|---|---|
| CITY.... | CIT | [COAST ] |
(1 row)
**Example: A simple fixed-width price list** (sample data: sample)
```sql
SELECT rpad(symbol, 8, '.') || lpad(close, 8) AS line
FROM ochl_day
WHERE time = '2026-09-09'
ORDER BY symbol;
```
Result:
| line (STRING) |
|---|
| CITY.... 504.6 |
| COAST... 380.1 |
| VOLT.... 187.3 |
(3 rows)
## repeat
Syntax:
```sql
repeat(text, n)
```
The text written `n` times one after another.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to repeat |
| `n` | whole number | how many times; 0 or less gives `''` |
- **Returns:** `STRING`.
- **NULL:** `NULL` when any argument is `NULL`.
- **Notes:** the result can be at most 16 MB; a bigger one is an error.
**Example**
```sql
SELECT repeat('ab', 3) AS a, repeat('नम', 2) AS nepali, repeat('-', 0) AS zero;
```
Result:
| a (STRING) | nepali (STRING) | zero (STRING) |
|---|---|---|
| ababab | नमनम | |
(1 row)
**Example: A text bar chart of the CITY close price** (sample data: sample)
```sql
SELECT time, close, repeat('#', floor((close - 500) / 2)) AS bar
FROM ochl_day
WHERE symbol = 'CITY'
ORDER BY time
LIMIT 6;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | bar (STRING) |
|---|---|---|
| 2025-12-28T00:00:00.000Z | 542.2 | ##################### |
| 2025-12-29T00:00:00.000Z | 539.7 | ################### |
| 2025-12-30T00:00:00.000Z | 539.3 | ################### |
| 2025-12-31T00:00:00.000Z | 542.4 | ##################### |
| 2026-08-23T00:00:00.000Z | 507.9 | ### |
| 2026-08-24T00:00:00.000Z | 510.3 | ##### |
(6 rows)
## reverse
Syntax:
```sql
reverse(text)
```
The characters of the text in the opposite order: `reverse('CITY')` is `YTIC`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (or BYTES) | the text |
- **Returns:** `STRING` (`BYTES` for a `BYTES` value, byte by byte).
- **NULL:** `reverse(NULL)` is `NULL`.
- **Notes:** it works character by character. A Nepali vowel sign is its own character, so it lands on the other side of its letter: the result of Nepali text does not read well.
**Example**
```sql
SELECT reverse('CITY') AS a, reverse('12345') AS b, reverse('नेपाल') AS nepali;
```
Result:
| a (STRING) | b (STRING) | nepali (STRING) |
|---|---|---|
| YTIC | 54321 | लापेन |
(1 row)
## concat
Syntax:
```sql
concat(value, value, ...)
```
Joins the values into one text. `NULL` values are skipped. Numbers, dates and times are turned into text first.
| Argument | Type | Meaning |
|---|---|---|
| `value` | any type | 1 to 64 values to join |
- **Returns:** `STRING`. Never `NULL`: when every value is `NULL`, the result is `''`.
- **NULL:** `NULL` values are skipped.
- **Notes:** `a || b` also joins text, but it gives `NULL` when one side is `NULL`. `concat()` with no values is an error.
**Example**
```sql
SELECT concat('CITY', '-', 2082) AS a, concat('a', NULL, 'b') AS null_skipped, concat(NULL) AS all_null, concat('Close: ', 512.5) AS b;
```
Result:
| a (STRING) | null_skipped (STRING) | all_null (STRING) | b (STRING) |
|---|---|---|---|
| CITY-2082 | ab | | Close: 512.5 |
(1 row)
**Example: Full name when the middle name is missing**
Setup:
```sql
CREATE TABLE app_users (id INT PRIMARY KEY AUTO, first_name STRING, middle_name STRING, last_name STRING);
INSERT INTO app_users (first_name, middle_name, last_name) VALUES ('Ram', 'Bahadur', 'Thapa'), ('Sita', NULL, 'Sharma');
```
```sql
SELECT id,
concat(first_name, ' ', middle_name, ' ', last_name) AS with_concat,
first_name || ' ' || middle_name || ' ' || last_name AS with_pipes
FROM app_users ORDER BY id;
```
Result:
| id (INT) | with_concat (STRING) | with_pipes (STRING) |
|---|---|---|
| 1 | Ram Bahadur Thapa | Ram Bahadur Thapa |
| 2 | Sita Sharma | NULL |
(2 rows)
## concat_ws
Syntax:
```sql
concat_ws(separator, value, value, ...)
```
Joins the values with `separator` between them. `NULL` values are skipped, and they get no separator. "ws" means "with separator".
| Argument | Type | Meaning |
|---|---|---|
| `separator` | STRING | the text put between two values |
| `value` | any type | 0 to 63 values to join |
- **Returns:** `STRING`.
- **NULL:** a `NULL` separator gives `NULL`. `NULL` values are skipped.
**Example**
```sql
SELECT concat_ws(', ', 'a', NULL, 'b') AS a, concat_ws('/', 2082, 83) AS fiscal_year, concat_ws(NULL, 'a', 'b') AS null_separator;
```
Result:
| a (STRING) | fiscal_year (STRING) | null_separator (STRING) |
|---|---|---|
| a, b | 2082/83 | NULL |
(1 row)
**Example: The full name from the example above, without double spaces**
Setup:
```sql
CREATE TABLE app_users (id INT PRIMARY KEY AUTO, first_name STRING, middle_name STRING, last_name STRING);
INSERT INTO app_users (first_name, middle_name, last_name) VALUES ('Ram', 'Bahadur', 'Thapa'), ('Sita', NULL, 'Sharma');
```
```sql
SELECT id, concat_ws(' ', first_name, middle_name, last_name) AS full_name FROM app_users ORDER BY id;
```
Result:
| id (INT) | full_name (STRING) |
|---|---|
| 1 | Ram Bahadur Thapa |
| 2 | Sita Sharma |
(2 rows)
## format
Syntax:
```sql
format(text, value, value, ...)
```
Fills the places in `text` with the values, as PostgreSQL does. Its main use is SQL text for [EXECUTE](procedure-language.md#execute) in a procedure.
| Place | Puts in | A `NULL` value |
|---|---|---|
| `%s` | the value as text | nothing (empty text) |
| `%I` | a table or column name, in double quotes when needed | an error |
| `%L` | a value in single quotes, quotes inside doubled | `NULL` (no quotes) |
| `%%` | one `%` | |
| `%2$s` | the 2nd value (any place type); the next place takes the 3rd | |
| `%10s`, `%-10s` | at least 10 characters, filled with spaces on the left (or right with `-`) | |
- **Returns:** `STRING`. A `NULL` text gives `NULL`.
- **Notes:** text made with `%I` and `%L` is safe to run as SQL: a quote inside a value stays part of the value. Too few values, or another letter after `%`, is an error.
**Example**
```sql
SELECT format('%s has %s rows', 'trades', 120) AS plain,
format('SELECT * FROM %I WHERE symbol = %L', 'Daily Prices', 'it''s') AS safe_sql,
format('[%-6s][%6s][%L]', 'ab', 'cd', NULL) AS widths;
```
Result:
| plain (STRING) | safe_sql (STRING) | widths (STRING) |
|---|---|---|
| trades has 120 rows | SELECT * FROM "Daily Prices" WHERE symbol = 'it''s' | [ab ][ cd][NULL] |
(1 row)
**Example: A NULL cannot be a name** (shows an error)
```sql
SELECT format('SELECT * FROM %I', NULL);
```
Result:
Error: `line 1, column 8: format: %I of format() got NULL: a NULL cannot be a table or column name`
## quote_ident
Syntax:
```sql
quote_ident(name)
```
The name as it can be written in SQL: as it is when it is all small letters, digits and `_` and not a reserved word, otherwise in double quotes (with `"` doubled). `quote_ident('close')` is `close`; `quote_ident('Close')` is `"Close"`.
- **Returns:** `STRING`.
## quote_literal, quote_nullable
Syntax:
```sql
quote_literal(value)
quote_nullable(value)
```
The value as SQL text in single quotes, with every `'` doubled: `quote_literal('it''s')` is `'it''s'`. A number is quoted too.
- **Returns:** `STRING`.
- **NULL:** `quote_literal(NULL)` is `NULL`; `quote_nullable(NULL)` is the text `NULL`, so the SQL still works.
**Example**
```sql
SELECT quote_ident('close') AS a, quote_ident('Daily Prices') AS b, quote_literal('O''Brien') AS c,
quote_literal(42) AS d, quote_nullable(NULL) AS e;
```
Result:
| a (STRING) | b (STRING) | c (STRING) | d (STRING) | e (STRING) |
|---|---|---|---|---|
| close | "Daily Prices" | 'O''Brien' | '42' | NULL |
(1 row)
**Example: One CSV line per trading day** (sample data: sample)
```sql
SELECT concat_ws(',', symbol, open, high, low, close) AS csv_line
FROM ochl_day
WHERE symbol = 'COAST'
ORDER BY time
LIMIT 3;
```
Result:
| csv_line (STRING) |
|---|
| COAST,420.3,426.6,417.6,424.8 |
| COAST,425.9,428.9,421.2,424.2 |
| COAST,423.6,426.1,420.1,423 |
(3 rows)
## || (join text)
Syntax:
```sql
text || text
```
Joins two values into one text: `'CITY' || '-' || 2082` is `CITY-2082`. A number or date on one side is turned into text.
| Operand | Type | Meaning |
|---|---|---|
| left, right | any type | the two values to join |
- **Returns:** `STRING`. Two `BYTES` values give `BYTES`.
- **NULL:** `NULL` on either side gives `NULL`. Use `concat` or `coalesce` when a value can be `NULL`.
- **Notes:** `||` has the **same** priority as `+` and `-`, and they are done from left to right. So `'a' || 1 + 2` is `('a' || 1) + 2`, which is an error. PostgreSQL does `+` first. Write `'a' || (1 + 2)`. MySQL reads `||` as OR unless a setting is changed; KDB always joins text.
**Example**
```sql
SELECT 'CITY' || '-' || 2082 AS a, 'Close: ' || 512.5 AS b, 'a' || NULL AS with_null, 'total ' || (1 + 2) AS brackets;
```
Result:
| a (STRING) | b (STRING) | with_null (STRING) | brackets (STRING) |
|---|---|---|---|
| CITY-2082 | Close: 512.5 | NULL | total 3 |
(1 row)
**Example: A link to each news article**
Setup:
```sql
CREATE TABLE news (id LONG PRIMARY KEY AUTO, title STRING NOT NULL, symbol STRING);
INSERT INTO news (title, symbol) VALUES ('City Bank profit rises 18%', 'CITY'), ('Market index closes at 2,840', NULL);
```
```sql
SELECT id, '/news/' || id || '-' || lower(replace(left(title, 20), ' ', '-')) AS url,
'Stock: ' || symbol AS stock_line,
'Stock: ' || coalesce(symbol, 'market') AS fixed
FROM news ORDER BY id;
```
Result:
| id (LONG) | url (STRING) | stock_line (STRING) | fixed (STRING) |
|---|---|---|---|
| 1 | /news/1-city-bank-profit-ris | Stock: CITY | Stock: CITY |
| 2 | /news/2-market-index-closes- | NULL | Stock: market |
(2 rows)
**Example: Without brackets, + is done after ||** (shows an error)
```sql
SELECT 'total ' || 1 + 2;
```
Result:
Error: `line 1, column 22: 'total 1' and '2' are not both numbers`
## initcap
Syntax:
```sql
initcap(text)
```
Every word gets a capital first letter and small letters after it: `initcap('city bank limited')` is `City Bank Limited`.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (a number is turned into text) | the text to change |
- **Returns:** `STRING`.
- **NULL:** `initcap(NULL)` is `NULL`.
- **Notes:** a word ends at anything that is not a letter or a digit, so `ram-bahadur` becomes `Ram-Bahadur`. Letters of every script with capitals change (`élan` becomes `Élan`); Nepali letters stay as they are. Short words like "and" are capitalised too, so a company name that is already written correctly (`Coastal Bank`) is better left alone.
**Example: Tidy up a name a user typed**
```sql
SELECT initcap('city bank limited') AS company,
initcap('RAM BAHADUR SHRESTHA') AS person,
initcap('ram-bahadur kc') AS with_hyphen,
initcap(NULL) AS nothing;
```
Result:
| company (STRING) | person (STRING) | with_hyphen (STRING) | nothing (STRING) |
|---|---|---|---|
| City Bank Limited | Ram Bahadur Shrestha | Ram-Bahadur Kc | NULL |
(1 row)
**Example: Company names in title case** (sample data: sample)
```sql
SELECT symbol, name, initcap(lower(name)) AS title_case
FROM securities
ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | name (STRING) | title_case (STRING) |
|---|---|---|
| ACME | Acme Investment Company | Acme Investment Company |
| CITY | City Bank Limited | City Bank Limited |
| COAST | Coastal Bank Limited | Coastal Bank Limited |
| TELCO | Northwind Telecom Limited | Northwind Telecom Limited |
| VOLT | Volt River Hydropower Limited | Volt River Hydropower Limited |
(5 rows)
## md5
Syntax:
```sql
md5(text)
```
The MD5 hash of the text: 32 lower-case hex digits. The same text always gives the same digits.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING (a number or time is turned into text) | the text to hash |
- **Returns:** `STRING`, always 32 characters.
- **NULL:** `md5(NULL)` is `NULL`. `md5('')` is `d41d8cd98f00b204e9800998ecf8427e`.
- **Notes:** **never use MD5 for passwords.** It is broken for that: use [password_hash](functions-security.md#password_hash). MD5 is fine as a short fingerprint of a row, to see whether a file or a record changed. It is here because PostgreSQL apps call it. For a strong fingerprint use [sha256](functions-security.md#sha256).
**Example: A short fingerprint for each daily candle** (sample data: sample)
```sql
SELECT symbol, close, md5(symbol || ':' || CAST(time AS STRING)) AS row_key
FROM ochl_day
WHERE time IN '2026-09-10'
ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | close (DOUBLE) | row_key (STRING) |
|---|---|---|
| CITY | 504.9 | 8c227f18b2f287859fb2008492cc191a |
| COAST | 376.1 | 7e95ad0adf2f1015de2a7078a3a221c6 |
| VOLT | 191.2 | 66b1f4d8c7af12a0fd60ade3702adfb1 |
(3 rows)
**Example: The same text always gives the same digits**
```sql
SELECT md5('CITY') AS a, md5('CITY') = md5('CITY') AS always_same, md5('') AS empty_text, md5(NULL) AS nothing;
```
Result:
| a (STRING) | always_same (BOOL) | empty_text (STRING) | nothing (STRING) |
|---|---|---|---|
| 859214628431995197c0558f7b5f8ffc | true | d41d8cd98f00b204e9800998ecf8427e | NULL |
(1 row)
## to_str
Syntax:
```sql
to_str(time, format)
```
Writes a time as text in the QuestDB format letters. QuestDB apps use this name; KDB's own name for the same job is [to_char](functions-date-time.md#to_char).
| Argument | Type | Meaning |
|---|---|---|
| `time` | `TIMESTAMP`, `DATE`, time text or milliseconds | the time to write |
| `format` | `STRING` | the pattern, see the table below |
| Letters | Gives | Example |
|---|---|---|
| `yyyy`, `yy` | year, 4 or 2 digits | `2026`, `26` |
| `MM`, `MMM` | month number, month name | `09`, `Sep` |
| `dd` | day of the month | `10` |
| `HH`, `hh` | hour 0-23, hour 1-12 | `05`, `11` |
| `mm`, `ss`, `SSS` | minute, second, millisecond | `15`, `00`, `123` |
| `EEE` | weekday name | `Thu` |
| `a` | AM or PM | `AM` |
| `'text'` | text in single quotes is written as it is | `'at'` |
- **Returns:** `STRING`.
- **NULL:** `NULL` when the time or the format is `NULL`.
- **Notes:** everything is **UTC**. For a local clock add its offset first, for example 20700000 for Nepal: `to_str(time + 20700000, 'HH:mm')`. Letters KDB does not know are written as they are. `UUU` (microseconds) is always `000`, because KDB keeps milliseconds.
**Example: Minute candles with a readable clock** (sample data: sample)
```sql
SELECT to_str(time, 'yyyy-MM-dd') AS day,
to_str(time, 'HH:mm') AS utc_clock,
to_str(time + 20700000, 'HH:mm') AS nepal_clock,
close
FROM ochl_minutes
WHERE symbol = 'CITY'
ORDER BY time
LIMIT 3;
```
Result:
| day (STRING) | utc_clock (STRING) | nepal_clock (STRING) | close (DOUBLE) |
|---|---|---|---|
| 2026-09-09 | 05:15 | 11:00 | 512.1 |
| 2026-09-09 | 05:16 | 11:01 | 511.9 |
| 2026-09-09 | 05:17 | 11:02 | 511.9 |
(3 rows)
**Example: A long date for a report heading** (sample data: sample)
```sql
SELECT to_str(time, 'EEE dd MMM yyyy') AS trading_day, symbol, close
FROM ochl_day
WHERE time IN '2026-09-10'
ORDER BY symbol;
```
Result:
| trading_day (STRING) | symbol (SYMBOL) | close (DOUBLE) |
|---|---|---|
| Thu 10 Sep 2026 | CITY | 504.9 |
| Thu 10 Sep 2026 | COAST | 376.1 |
| Thu 10 Sep 2026 | VOLT | 191.2 |
(3 rows)
## string_to_array
Syntax:
```sql
string_to_array(text, separator)
string_to_array(text, separator, null_text)
```
Cuts the text into a list at every `separator`. The PostgreSQL way to turn `'CITY,COAST'` into a list of symbols.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to cut |
| `separator` | STRING | the text between the parts; `NULL` cuts into single characters |
| `null_text` | STRING | a part equal to this becomes `NULL` |
- **Returns:** an array (a `JSON` list). See [arrays](functions-postgres.md#arrays).
- **NULL:** `string_to_array(NULL, ',')` is `NULL`. Empty text gives an empty list `[]`.
- **Notes:** an empty separator gives a list with the whole text in it. To go the other way use [array_to_string](functions-postgres.md#array_to_string); to make rows out of the list use [unnest](functions-postgres.md#unnest). To take one part only, [split_part](#split-part) is simpler.
**Example: A watchlist typed as one line**
```sql
SELECT string_to_array('CITY,COAST,VOLT', ',') AS symbols,
cardinality(string_to_array('CITY,COAST,VOLT', ',')) AS how_many,
string_to_array('CITY,,VOLT', ',', '') AS empty_part_is_null,
string_to_array('MARKET', NULL) AS every_character;
```
Result:
| symbols (TEXT[]) | how_many (LONG) | empty_part_is_null (TEXT[]) | every_character (TEXT[]) |
|---|---|---|---|
| ["CITY","COAST","VOLT"] | 3 | ["CITY",null,"VOLT"] | ["M","A","R","K","E","T"] |
(1 row)
**Example: Show the companies of a typed watchlist** (sample data: sample)
```sql
SELECT symbol, name, sector
FROM securities
WHERE symbol IN (SELECT unnest(string_to_array('CITY,VOLT', ',')))
ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | name (STRING) | sector (STRING) |
|---|---|---|
| CITY | City Bank Limited | Banking |
| VOLT | Volt River Hydropower Limited | Energy |
(2 rows)
## string_to_table
Syntax:
```sql
string_to_table(text, separator)
string_to_table(text, separator, null_text)
```
The same cut as [string_to_array](#string-to-array), but one **row** per part instead of one list. Use it in `FROM` or in the `SELECT` list.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to cut |
| `separator` | STRING | the text between the parts; `NULL` cuts into single characters |
| `null_text` | STRING | a part equal to this becomes `NULL` |
- **Returns:** rows of `STRING`, one per part. In `FROM` the column is named after the function, or after the alias: `AS t(sym)`.
- **NULL:** `NULL` text gives no rows. Empty text gives no rows.
- **Notes:** in `FROM` the arguments must be constants. For a column write `SELECT id, string_to_table(col, ',') FROM t`.
**Example: A typed watchlist as rows**
```sql
SELECT sym FROM string_to_table('CITY,COAST,VOLT', ',') AS t(sym);
```
Result:
| sym (STRING) |
|---|
| CITY |
| COAST |
| VOLT |
(3 rows)
## ascii
Syntax:
```sql
ascii(text)
```
The number of the first character of the text (its Unicode code point). `ascii('A')` is `65`, `ascii('न')` is `2344`.
- **Returns:** `LONG`. Empty text gives `0`.
- **NULL:** `ascii(NULL)` is `NULL`.
**Example**
```sql
SELECT ascii('A') AS a, ascii('é') AS e_accent, ascii('नेपाल') AS na, ascii('') AS empty;
```
Result:
| a (LONG) | e_accent (LONG) | na (LONG) | empty (LONG) |
|---|---|---|---|
| 65 | 233 | 2344 | 0 |
(1 row)
## chr
Syntax:
```sql
chr(number)
```
The character with this Unicode number: `chr(65)` is `A`, `chr(2344)` is `न`. The opposite of [ascii](#ascii).
- **Returns:** `STRING` of one character.
- **Errors (as PostgreSQL, SQLSTATE 54000):** `0` ("null character not permitted"), a negative number, a number above 1114111, and the numbers 55296-57343 (they are not characters on their own).
**Example**
```sql
SELECT chr(65) AS a, chr(2344) AS na, chr(10) = E'\n' AS newline;
```
Result:
| a (STRING) | na (STRING) | newline (BOOL) |
|---|---|---|
| A | न | true |
(1 row)
**Example: 0 is not a character** (shows an error)
```sql
SELECT chr(0);
```
Result:
Error: `line 1, column 8: chr: null character not permitted`
## translate
Syntax:
```sql
translate(text, from, to)
```
Swaps single characters: each character of `from` becomes the character at the same place in `to`. A character of `from` with no partner in `to` is removed.
| Argument | Type | Meaning |
|---|---|---|
| `text` | STRING | the text to change |
| `from` | STRING | the characters to find |
| `to` | STRING | what they become, place by place |
- **Returns:** `STRING`.
- **Notes:** `translate('12345', '143', 'ax')` is `a2x5`: 1 becomes a, 4 becomes x, 3 is removed. A character named twice in `from` uses its first partner. It works on characters of any language. When `from` and `to` are written in the query, the swap table is made once for the whole query.
**Example**
```sql
SELECT translate('12345', '143', 'ax') AS pg_example,
translate('résumé', 'é', 'e') AS no_accents,
translate('98-41-234567', '-', '') AS digits_only;
```
Result:
| pg_example (STRING) | no_accents (STRING) | digits_only (STRING) |
|---|---|---|
| a2x5 | resume | 9841234567 |
(1 row)
## overlay
Syntax:
```sql
overlay(text PLACING new_text FROM start)
overlay(text PLACING new_text FROM start FOR count)
overlay(text, new_text, start [, count])
```
Puts `new_text` into `text` at character `start`, in place of `count` characters. Without `FOR`, as many characters as `new_text` has are replaced.
- **Returns:** `STRING` (`BYTES` when `text` is `BYTES`: then it counts bytes).
- **Notes:** `overlay('Txxxxas' PLACING 'hom' FROM 2 FOR 4)` is `Thomas`. `FOR 0` inserts without removing anything. A start past the end adds at the end.
- **Errors:** a start of `0` or less: "negative substring length not allowed" (SQLSTATE 22011), as PostgreSQL.
**Example**
```sql
SELECT overlay('Txxxxas' PLACING 'hom' FROM 2 FOR 4) AS thomas,
overlay('98XXXXXX45' PLACING '******' FROM 3) AS masked,
overlay('abc' PLACING '-' FROM 2 FOR 0) AS inserted;
```
Result:
| thomas (STRING) | masked (STRING) | inserted (STRING) |
|---|---|---|
| Thomas | 98******45 | a-bc |
(1 row)
## normalize
Syntax:
```sql
normalize(text)
normalize(text, NFC | NFD | NFKC | NFKD)
```
Brings text into one Unicode **normal form**. The same letter can be written two ways: `á` as one character, or as `a` followed by an accent mark. They look the same but do not compare equal until both are normalized.
| Form | What it does |
|---|---|
| `NFC` (the default) | joins a letter and its marks into one character where Unicode has one |
| `NFD` | takes such characters apart: letter, then marks |
| `NFKC` | like NFC, and turns look-alike characters into plain ones: `fi` → `fi`, `²` → `2`, `①` → `1` |
| `NFKD` | like NFD with the same look-alike rule |
- **Returns:** `STRING`.
- **Notes:** the form is written as a bare word, as in PostgreSQL; `'NFC'` in quotes works too. Text of plain English letters comes back at once, unchanged.
- **Errors:** another form name: "invalid normalization form" (SQLSTATE 22023).
**Example**
```sql
SELECT normalize(U&'\0061\0301', NFC) = U&'\00E1' AS same_letter,
length(normalize('á', NFD)) AS letters_nfd,
normalize('file ²', NFKC) AS plain;
```
Result:
| same_letter (BOOL) | letters_nfd (LONG) | plain (STRING) |
|---|---|---|
| true | 2 | file 2 |
(1 row)
`U&'...'` is PostgreSQL's way to write characters by number: `\XXXX` (4 hex digits) or `\+XXXXXX` (6), and `\\` for a backslash.
## to_ascii
Syntax:
```sql
to_ascii(text)
to_ascii(text, 'LATIN1')
```
The text with accents taken off, in plain ASCII: `to_ascii('Karél')` is `Karel`.
- **Returns:** `STRING`.
- **Notes:** Latin-1 letters follow PostgreSQL's table (`Æ` → `A`, `ß` → `B`, `ÿ` → `y`). Other letters with accents keep their letter (`ě` → `e`). A PostgreSQL database in UTF-8 refuses `to_ascii` altogether; KDB answers as a PostgreSQL database in LATIN1 does.
- **Errors:** a character with no ASCII form, such as Nepali letters: "has no ASCII equivalent" (SQLSTATE 22P05). Encodings other than LATIN1 are not supported (0A000).
**Example**
```sql
SELECT to_ascii('Karél') AS a, to_ascii('Škoda Ørsted') AS b;
```
Result:
| a (STRING) | b (STRING) |
|---|---|
| Karel | Skoda Orsted |
(1 row)
## bit_length
Syntax:
```sql
bit_length(text)
```
The size of the text in bits: 8 times [octet_length](#octet-length). `bit_length('jose')` is `32`; `bit_length('न')` is `24`.
- **Returns:** `LONG`. Works on `BYTES` too.
**Example**
```sql
SELECT bit_length('jose') AS english, bit_length('न') AS nepali_letter;
```
Result:
| english (LONG) | nepali_letter (LONG) |
|---|---|
| 32 | 24 |
(1 row)
## to_hex
Syntax:
```sql
to_hex(number)
```
A whole number in hexadecimal (base 16), small letters: `to_hex(255)` is `ff`.
- **Returns:** `STRING`.
- **Notes:** a negative number is written as PostgreSQL does: in 32 bits when it fits an `INT` (`to_hex(-1)` is `ffffffff`), else in 64 bits.
**Example**
```sql
SELECT to_hex(255) AS ff, to_hex(4096) AS big, to_hex(-1) AS minus_one;
```
Result:
| ff (STRING) | big (STRING) | minus_one (STRING) |
|---|---|---|
| ff | 1000 | ffffffff |
(1 row)
## parse_ident
Syntax:
```sql
parse_ident(name)
parse_ident(name, strict)
```
Splits a qualified name like `"SomeSchema".someTable` into its parts, the PostgreSQL way: quotes are taken off, a name without quotes is made lower case, `""` inside quotes is one quote.
- **Returns:** an array of `STRING` (a `JSON` list).
- **Notes:** with `strict` = `false`, text after the name is ignored: `parse_ident('a b', false)` is `{a}`.
- **Errors:** text that is not a valid name ("string is not a valid identifier", SQLSTATE 22023).
**Example**
```sql
SELECT parse_ident('"SomeSchema".someTable') AS parts,
parse_ident('market.Trades')[2] AS table_part;
```
Result:
| parts (TEXT[]) | table_part (STRING) |
|---|---|
| ["SomeSchema","sometable"] | trades |
(1 row)
## to_char for numbers
Syntax:
```sql
to_char(number, format)
```
Writes a number as text with a picture of the digits, exactly as PostgreSQL does. (For times see [to_char](functions-date-time.md#to_char).)
| Code | Meaning |
|---|---|
| `9` | a digit; a blank when the number has no digit here |
| `0` | a digit, `0` when the number has none (leading zeros) |
| `.` or `D` | the decimal point |
| `,` or `G` | the thousands separator (left out before the first digit) |
| `FM` | no padding blanks and no trailing zeros |
| `S`, `MI`, `PL`, `SG` | the sign next to the number; `-` at that place; `+` at that place; `+` or `-` at that place |
| `PR` | a negative number in angle brackets: `<485>` |
| `L` | the money sign `$` |
| `RN` / `rn` | Roman numerals (1 to 3999) |
| `TH` / `th` | the ordinal ending: `1st`, `22nd`, `13th` |
| `V` | multiplies by 10 for each `9` after it |
| `EEEE` | scientific notation: `4.86e-04` |
| `"text"` | text as it is |
- **Returns:** `STRING`. Without `FM` the text starts with a place for the sign: `to_char(485, '999')` is ` 485`.
- **Notes:** a number with more digits than the picture has places shows `#` in every place. A `DECIMAL` (and a number written in the query, such as `1234.5`) is exact and rounds halves away from zero; a `DOUBLE` rounds as PostgreSQL's float8 does (at most 15 digits). A whole number with a time format (`'YYYY-MM-DD'`) is still read as milliseconds since 1970, as before. A format written in the query is read once for all rows.
- **Errors:** PostgreSQL's (SQLSTATE 42601): "multiple decimal points", "cannot use "S" twice", ...
**Example**
```sql
SELECT to_char(1234.5, 'FM999,999.00') AS money,
to_char(-485, '999PR') AS bracket,
to_char(7, 'FM000') AS padded,
to_char(0.0004859, '9.99EEEE') AS sci,
to_char(2026, 'FMRN') AS roman,
to_char(22, 'FM99th') AS ordinal;
```
Result:
| money (STRING) | bracket (STRING) | padded (STRING) | sci (STRING) | roman (STRING) | ordinal (STRING) |
|---|---|---|---|---|---|
| 1,234.50 | <485> | 007 | 4.86e-04 | MMXXVI | 22nd |
(1 row)
**Example: Turnover in a report** (sample data: sample)
```sql
SELECT symbol, to_char(sum(close * volume), 'FM999,999,999,990.00') AS turnover
FROM ochl_day
GROUP BY symbol
ORDER BY symbol
LIMIT 3;
```
Result:
| symbol (SYMBOL) | turnover (STRING) |
|---|---|
| CITY | 1,012,051,301.00 |
| COAST | 640,748,179.00 |
| VOLT | 326,540,767.00 |
(3 rows)