Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

SQL (PostgreSQL, MySQL, SQLite)  Module 3 – Data types and built-in functions

Text functions and Unicode

Measure, slice, search, replace and join text in PostgreSQL, MySQL and SQLite, handle NULLs when concatenating, and count Hindi text in characters and bytes.

  • Beginner
  • 25 minutes
  • Examples run with sql.js 1.14.2
  • By MySmartCoPilot
Free: 25 minutes

What you will learn

  • Measure, slice, search and replace text
  • Join strings safely when values can be NULL
  • Handle Indian-language text and the difference between characters and bytes
  • Format numbers with digit grouping for display

Before you start

What you’ll learn · Before you start

What you will learn

  • Measure, slice, search and replace text
  • Join strings safely when values can be NULL
  • Handle Indian-language text and the difference between characters and bytes
  • Format numbers with digit grouping for display

Before you start

On this page

Text functions measure, cut, search, change and join strings: length(), substr(), instr(), replace(), upper(), || and concat() cover most daily work. All three engines of this track have them, often under other names, and they trip over the same two things. The first is NULL: one missing value inside || makes the whole result NULL. The second is text beyond English letters, where one written letter can be several Unicode code points and each code point several bytes, so a Hindi word has three different lengths depending on who counts. This lesson runs each function on real names, including Devanagari, and compares the engines in a table at the end.

Each query here runs in your browser with SQLite 3.49.1, from sql.js 1.14.2, Devanagari included; what this lesson says about PostgreSQL 18 and MySQL 8.4 rests on their documentation.

Length: characters and bytes

length() counts the characters of a text value, where a character means a Unicode code point, and octet_length() counts the bytes the text takes in UTF-8:

Five words, counted in characters and in bytes SQL · lengths.sql
-- length() counts characters (Unicode code points); octet_length() counts the bytes of their UTF-8 encoding.
CREATE TABLE words (word TEXT);
INSERT INTO words VALUES ('Asha'), ('café'), ('₹500'), ('தமிழ்'), ('नमस्ते');

SELECT length(word) AS characters, octet_length(word) AS bytes, word
FROM words;

Output

┌────────────┬───────┬────────┐
│ characters │ bytes │  word  │
├────────────┼───────┼────────┤
│ 4          │ 4     │ Asha   │
│ 4          │ 5     │ café   │
│ 4          │ 6     │ ₹500   │
│ 5          │ 15    │ தமிழ்  │
│ 6          │ 18    │ नमस्ते │
└────────────┴───────┴────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < lengths.sql

English letters take one byte each, so Asha is 4 and 4. The é of café, the rupee sign and every Tamil and Devanagari code point take more: नमस्ते is 6 characters and 18 bytes. A reader, though, sees neither number:

The Hindi word namaste: 3 letters for a reader, 6 code points for length(), 18 bytes for octet_length().What a reader sees: 3 lettersna · ma · stelength(): 6 code pointsWhat octet_length() counts: 18 bytes3 bytes of UTF-8 for each code pointU+0928letter naU+092Eletter maU+0938letter saU+094DviramaU+0924letter taU+0947vowel sign ethe last letter isfour code pointsencoded as UTF-8

One Hindi word, three different counts

Text description of the diagram

The diagram shows the word namaste, written in Devanagari, counted three ways, from top to bottom.

  1. A reader sees 3 letters: na, ma and ste. The last one is a conjunct that joins two consonants and a vowel sign into one written shape.
  2. length() counts 6 Unicode code points: U+0928 letter na, U+092E letter ma, U+0938 letter sa, U+094D virama (the sign that joins sa to the next consonant), U+0924 letter ta and U+0947 vowel sign e. The arrow between the first two boxes notes that the last written letter is four of these code points.
  3. octet_length() counts 18 bytes, because UTF-8 stores each of these code points in 3 bytes.

So the same word has a length of 6 in SQL, takes 18 bytes of storage, and looks like 3 letters on screen. MySQL's LENGTH() returns the 18, PostgreSQL's and SQLite's length() the 6.

The current Unicode rules for what a reader perceives as one letter, called grapheme clusters in Unicode Standard Annex #29, group नमस्ते into 3, because स्ते joins two consonants and a vowel sign. SQL functions count code points, and this example lists them:

The code points of one Hindi word SQL · code_points.sql
-- The six code points of one Hindi word, one row each. A row's text is a single code point, so the
-- virama (U+094D) and the vowel sign (U+0947) appear on their own, without the letter they belong to.
CREATE TABLE positions (i INTEGER);
INSERT INTO positions VALUES (1), (2), (3), (4), (5), (6);

SELECT i,
       printf('U+%04X', unicode(substr('नमस्ते', i, 1))) AS code_point,
       octet_length(substr('नमस्ते', i, 1))               AS bytes,
       substr('नमस्ते', i, 1)                             AS text
FROM positions;

-- Cutting after four code points leaves the virama dangling: the result is not a whole word.
SELECT substr('नमस्ते', 1, 3) AS first_three, substr('नमस्ते', 1, 4) AS first_four;

Output

┌───┬────────────┬───────┬──────┐
│ i │ code_point │ bytes │ text │
├───┼────────────┼───────┼──────┤
│ 1 │ U+0928     │ 3     │ न    │
│ 2 │ U+092E     │ 3     │ म    │
│ 3 │ U+0938     │ 3     │ स    │
│ 4 │ U+094D     │ 3     │ ्    │
│ 5 │ U+0924     │ 3     │ त    │
│ 6 │ U+0947     │ 3     │ े    │
└───┴────────────┴───────┴──────┘
┌─────────────┬────────────┐
│ first_three │ first_four │
├─────────────┼────────────┤
│ नमस         │ नमस्       │
└─────────────┴────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < code_points.sql

The fourth code point, U+094D, is the virama, a sign that joins the consonant before it to the next one. Taken alone, it and the vowel sign U+0947 show up as marks without a letter. That is also why substr() can damage a word: four code points from the start end in a dangling virama, which is not a word anyone wrote. Cut text for display only at spaces, or let the application cut it with a library that knows grapheme clusters.

The engines also disagree about the word “length”. PostgreSQL’s length() and char_length() count characters, as SQLite’s length() does. MySQL’s LENGTH() counts bytes, so it returns 18 for नमस्ते, while its CHAR_LENGTH() returns 6. A check such as LENGTH(name) <= 50 therefore allows only 16 Devanagari characters in MySQL.

Taking parts of a string

substr() from the start and from the end, and instr() SQL · slicing.sql
-- Parts of a string: substr() counts from the start, or from the end with a negative position.
SELECT substr('Bengaluru', 1, 4)  AS first_four,
       substr('Bengaluru', -4)    AS last_four,
       substr('Bengaluru', 5)     AS from_fifth,
       instr('Bengaluru', 'luru') AS found_at,
       instr('Bengaluru', 'Luru') AS other_case;

Output

┌────────────┬───────────┬────────────┬──────────┬────────────┐
│ first_four │ last_four │ from_fifth │ found_at │ other_case │
├────────────┼───────────┼────────────┼──────────┼────────────┤
│ Beng       │ luru      │ aluru      │ 6        │ 0          │
└────────────┴───────────┴────────────┴──────────┴────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < slicing.sql

substr(text, start, count) counts positions from 1. Without a count it runs to the end, and a negative start counts from the end, so substr('Bengaluru', -4) is luru. instr(text, part) returns the position of the first match, or 0 when there is none, and it respects case, which is why 'Luru' is not found.

PostgreSQL and MySQL spell these differently. Both have right(text, n) and left(text, n) for the ends of a string. PostgreSQL finds text with strpos(text, part) or position(part IN text). MySQL has LOCATE(part, text) and INSTR(text, part), with the arguments in opposite orders, an easy pair to mix up when moving queries between engines. To take the second field of a comma-separated value, PostgreSQL has split_part(s, ',', 2), MySQL nests SUBSTRING_INDEX() twice, SUBSTRING_INDEX(SUBSTRING_INDEX(s, ',', 2), ',', -1), and SQLite needs instr() to find the commas and substr() to cut between them.

Cleaning text and changing case

trim, ltrim, replace and upper SQL · clean_text.sql
-- Trimming, replacing and changing case. SQLite's upper() and lower() change only ASCII letters.
SELECT trim('  Pune  ')               AS trimmed,
       ltrim('000452', '0')            AS no_zeros,
       replace('98765-43210', '-', '') AS digits,
       upper('Asha Rao')               AS ascii,
       upper('straße, café')           AS other;

Output

┌─────────┬──────────┬────────────┬──────────┬──────────────┐
│ trimmed │ no_zeros │   digits   │  ascii   │    other     │
├─────────┼──────────┼────────────┼──────────┼──────────────┤
│ Pune    │ 452      │ 9876543210 │ ASHA RAO │ STRAßE, CAFé │
└─────────┴──────────┴────────────┴──────────┴──────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < clean_text.sql

trim() removes spaces from both ends, and with a second argument it removes those characters instead, so ltrim('000452', '0') drops leading zeros. replace() swaps every occurrence of one string for another, here deleting the hyphen in a phone number. The last column shows a SQLite limit: upper() and lower() change only the 26 ASCII letters, so ß and é stay as they were. PostgreSQL and MySQL convert other letters too, following the database’s locale in PostgreSQL and the character set in MySQL. Devanagari and Tamil have no upper and lower case, so these functions leave them alone in every engine.

Joining text, and NULL

|| joins two strings in PostgreSQL and SQLite. Like arithmetic, it returns NULL when either side is NULL:

Three ways to join name parts SQL · concat_nulls.sql
-- Joining name parts when the middle name can be missing.
CREATE TABLE people (given TEXT, middle TEXT, surname TEXT);
INSERT INTO people VALUES ('Asha', NULL, 'Rao'), ('Vikram', 'Singh', 'Rathore');

SELECT given || ' ' || middle || ' ' || surname AS pipes,
       concat(given, ' ', middle, ' ', surname)  AS with_concat,
       concat_ws(' ', given, middle, surname)    AS with_concat_ws
FROM people;

Output

┌──────────────────────┬──────────────────────┬──────────────────────┐
│        pipes         │     with_concat      │    with_concat_ws    │
├──────────────────────┼──────────────────────┼──────────────────────┤
│                      │ Asha  Rao            │ Asha Rao             │
│ Vikram Singh Rathore │ Vikram Singh Rathore │ Vikram Singh Rathore │
└──────────────────────┴──────────────────────┴──────────────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < concat_nulls.sql

Asha has no middle name, so the || version of her full name is NULL (the empty cell). concat() treats NULL as an empty string, which keeps her name but leaves two spaces between the parts. concat_ws(), “concatenate with separator”, takes the separator first and skips NULL values entirely, so it gives Asha Rao. When an empty string should count as missing too, turn it into NULL first with nullif(middle, '').

Warning

In MySQL, || is not concatenation by default but a deprecated spelling of logical OR, so 'Asha' || 'Rao' returns 0. The SQL mode PIPES_AS_CONCAT, also part of the ANSI mode, makes it join strings. MySQL’s CONCAT() also behaves differently from the others: it returns NULL as soon as one argument is NULL. CONCAT_WS() skips NULLs in all three engines, so it is the portable choice.

Version note

concat() and concat_ws() exist only from SQLite 3.44.0 onwards. This track’s browser engine is version 3.49.1, so they work here, but a SQLite built into an older phone app rejects them, while || works in every version.

Padding, cutting and masking with printf

SQLite has no lpad() or repeat(), but printf() covers both and more. Its widths and precisions count bytes, unless you add the ! flag, which makes them count characters:

Widths and precisions in bytes and in characters SQL · bytes_or_characters.sql
-- printf() measures widths and precisions in bytes, or in characters with the ! flag.
CREATE TABLE formats (how TEXT, pattern TEXT);
INSERT INTO formats VALUES
  ('pad to 8 bytes',      '[%-8s]'),
  ('pad to 8 characters', '[%-!8s]'),
  ('first 3 bytes',       '%.3s'),
  ('first 3 characters',  '%!.3s');

SELECT how, printf(pattern, 'Asha') AS latin, printf(pattern, 'नमस्ते') AS hindi
FROM formats;

Output

┌─────────────────────┬────────────┬────────────┐
│         how         │   latin    │   hindi    │
├─────────────────────┼────────────┼────────────┤
│ pad to 8 bytes      │ [Asha    ] │ [नमस्ते]   │
│ pad to 8 characters │ [Asha    ] │ [नमस्ते  ] │
│ first 3 bytes       │ Ash        │ न          │
│ first 3 characters  │ Ash        │ नमस        │
└─────────────────────┴────────────┴────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < bytes_or_characters.sql

[%-8s] pads Asha to 8 bytes, but नमस्ते already has 18 bytes, so it gets no padding at all, and %.3s keeps just three bytes, one Devanagari code point. With ! both count characters. When you align columns of names in a report, use the ! form, or pad in the application. PostgreSQL and MySQL pad with lpad() and rpad(), which count characters.

Two more printf() tricks build masks. %.*c repeats a character as many times as the argument before it says, and %06d pads a number with zeros to six digits. Here the local part of an email address is hidden:

Keep the first letter and the domain of an email address SQL · masks.sql
-- Hiding part of an email address: keep the first letter and the domain.
CREATE TABLE contacts (email TEXT);
INSERT INTO contacts VALUES ('asha.rao@example.com'), ('vk@example.in'), (NULL);

SELECT email,
       substr(email, 1, 1)
         || printf('%.*c', instr(email, '@') - 2, '*')
         || substr(email, instr(email, '@')) AS masked
FROM contacts;

Output

┌──────────────────────┬──────────────────────┐
│        email         │        masked        │
├──────────────────────┼──────────────────────┤
│ asha.rao@example.com │ a*******@example.com │
│ vk@example.in        │ v*@example.in        │
│                      │                      │
└──────────────────────┴──────────────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < masks.sql

instr(email, '@') finds the at sign, the stars replace everything between the first letter and it, and NULL stays NULL, because any || with NULL is NULL. A masked value in a report is not protection on its own: whoever can query the table still sees the original.

Text that looks the same but is not

Unicode sometimes offers two ways to write one letter. The Hindi letter ज़ exists as a single code point, and also as ज followed by the nukta sign, and keyboards and apps produce both:

One letter written two ways SQL · look_alike.sql
-- Two ways to write the letter ज़: one code point, or ज followed by the nukta sign.
SELECT char(2395) = char(2332, 2364) AS equal,
       length(char(2395))            AS length_one,
       length(char(2332, 2364))      AS length_two,
       char(2395)                    AS one_code_point,
       char(2332, 2364)              AS two_code_points;

Output

┌───────┬────────────┬────────────┬────────────────┬─────────────────┐
│ equal │ length_one │ length_two │ one_code_point │ two_code_points │
├───────┼────────────┼────────────┼────────────────┼─────────────────┤
│ 0     │ 1          │ 2          │ ज़              │ ज़              │
└───────┴────────────┴────────────┴────────────────┴─────────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < look_alike.sql

The two strings look identical, yet = says they differ, and their lengths are 1 and 2. A search for one form misses rows stored in the other. Unicode defines normalization forms that settle on one spelling, in Unicode Standard Annex #15; PostgreSQL applies them with normalize(text, NFC), and the NFC form of both strings here is the same two-code-point sequence. SQLite has no normalization function, so normalize text in the application before you store or compare it.

Formatting numbers for people

printf('%,d', n) adds a comma before every third digit from the right, the international style. Indian reports group the digits differently, and a CASE on the size of the number builds that style from integer arithmetic:

Thousands grouping and Indian grouping SQL · grouping.sql
-- Digit grouping for display: in thousands, and in the Indian style (groups of two after the first three).
CREATE TABLE totals (rupees INTEGER);
INSERT INTO totals VALUES (999), (45000), (1234567), (98765432), (9876543210);

SELECT rupees,
       printf('%,d', rupees) AS thousands,
       CASE
         WHEN rupees < 1000     THEN printf('%d', rupees)
         WHEN rupees < 100000   THEN printf('%d,%03d', rupees / 1000, rupees % 1000)
         WHEN rupees < 10000000 THEN printf('%d,%02d,%03d', rupees / 100000, rupees / 1000 % 100, rupees % 1000)
         ELSE printf('%d,%02d,%02d,%03d', rupees / 10000000, rupees / 100000 % 100, rupees / 1000 % 100, rupees % 1000)
       END AS indian
FROM totals;

Output

┌────────────┬───────────────┬───────────────┐
│   rupees   │   thousands   │    indian     │
├────────────┼───────────────┼───────────────┤
│ 999        │ 999           │ 999           │
│ 45000      │ 45,000        │ 45,000        │
│ 1234567    │ 1,234,567     │ 12,34,567     │
│ 98765432   │ 98,765,432    │ 9,87,65,432   │
│ 9876543210 │ 9,876,543,210 │ 987,65,43,210 │
└────────────┴───────────────┴───────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < grouping.sql

Lakh and crore

The Indian style puts a comma after the last three digits and then after every two: 12,34,567 is 12 lakh 34 thousand 567, and 987,65,43,210 is 987 crore 65 lakh 43 thousand 210. PostgreSQL’s to_char(n, 'FM99,99,999') places the commas where the pattern puts them, and MySQL’s FORMAT(n, 0, 'en_IN') groups in the Indian style. Format only for display: a formatted amount is text, and it sorts and adds up as text.

How the engines differ

Text functions in PostgreSQL, MySQL and SQLite
Criterion PostgreSQL 18MySQL 8.4SQLite 3.49
Characters length(s)CHAR_LENGTH(s)length(s)
Bytes octet_length(s)LENGTH(s)octet_length(s)
Join a || bCONCAT(a, b)a || b
concat() and NULL Skips itReturns NULLSkips it
Last 4 characters right(s, 4)RIGHT(s, 4)substr(s, -4)
Find a part p strpos(s, p)LOCATE(p, s)instr(s, p)
Pad with zeros lpad(s, 6, '0')LPAD(s, 6, '0')printf('%06d', n)
Split on a comma split_part()SUBSTRING_INDEX()instr(), substr()
Case of é and ß ChangedChangedUnchanged
Unicode Character Inspector Paste a name to see each of its code points, with their numbers and names.

Your turn

Exercise · Easy · SQL

Display names and masked phone numbers

The table customers has the columns id, given_name, surname and phone, all text except id. Some customers have no surname and some have no phone (NULL), and names may be written in any script, such as Devanagari.

Write a query that returns, for each customer:

- id; - display_name: the surname, a comma and a space, then the given name, such as Rao, Asha; a customer without a surname shows the given name alone; - masked_phone: the phone with every character except the last four replaced by *, so 9876543210 becomes ******3210 and +919812345678 becomes *********5678. A phone of four characters or fewer is shown unchanged, and a missing phone stays NULL.

The order of the rows does not matter. The sample tests include missing surnames, missing phones, a short phone and Devanagari names.

Starter code · display_names.sql

-- display_name: 'Surname, Given' (the given name alone without a surname);
-- masked_phone: every character but the last four replaced by '*'.
SELECT id,
       given_name AS display_name,
       phone AS masked_phone
FROM customers;
The sample tests · tests.yaml
# Sample tests: each one builds a small customers table, runs your query on it and compares the rows it returns (in
# any order).
tests:
  - name: shows surname first and masks all but the last four characters
    setup: |
      CREATE TABLE customers (id INTEGER PRIMARY KEY, given_name TEXT NOT NULL, surname TEXT, phone TEXT);
      INSERT INTO customers VALUES (1, 'Asha', 'Rao', '9876543210'), (2, 'Vikram', 'Rathore', '+919812345678');
    expected:
      columns: [id, display_name, masked_phone]
      rows:
        - [1, 'Rao, Asha', '******3210']
        - [2, 'Rathore, Vikram', '*********5678']
  - name: handles a missing surname, a missing phone and a short phone
    setup: |
      CREATE TABLE customers (id INTEGER PRIMARY KEY, given_name TEXT NOT NULL, surname TEXT, phone TEXT);
      INSERT INTO customers VALUES (1, 'Bilal', NULL, '9123456780'), (2, 'Meena', 'Iyer', NULL), (3, 'Joseph', 'Dsouza', '1234');
    expected:
      columns: [id, display_name, masked_phone]
      rows:
        - [1, 'Bilal', '******6780']
        - [2, 'Iyer, Meena', null]
        - [3, 'Dsouza, Joseph', '1234']
  - name: works for names in Devanagari
    setup: |
      CREATE TABLE customers (id INTEGER PRIMARY KEY, given_name TEXT NOT NULL, surname TEXT, phone TEXT);
      INSERT INTO customers VALUES (1, 'प्रिया', 'शर्मा', '9988776655'), (2, 'अर्जुन', NULL, NULL);
    expected:
      columns: [id, display_name, masked_phone]
      rows:
        - [1, 'शर्मा, प्रिया', '******6655']
        - [2, 'अर्जुन', null]
A hint

surname || ', ' is NULL when the surname is missing, so coalesce(surname || ', ', '') || given_name covers both cases. For the mask, printf('%.*c', n, '*') repeats the star n times and substr(phone, -4) keeps the last four characters; decide in a CASE what happens when length(phone) is 4 or less.

The sample tests run on this device, in your browser (sql.js): nothing is sent to mysmartcopilot.com. The first run downloads SQLite (about 0.7 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 How many bytes does SQLite's octet_length('नमस्ते') return? The word has 6 Unicode code points.

    Type a number.

    Show the answer to question 1

    Answer: 18

    Every Devanagari code point takes 3 bytes in UTF-8, so 6 code points take 18 bytes. length() returns 6, and a reader sees 3 letters.

  2. Question 2 of 6 What does MySQL 8.4 return for LENGTH('नमस्ते') in a utf8mb4 string?

    Choose one answer.

    Show the answer to question 2

    Answer: 18, because MySQL's LENGTH() counts bytes

    MySQL's LENGTH() is a byte count; CHAR_LENGTH() counts characters and returns 6. PostgreSQL's and SQLite's length() count characters.

  3. Question 3 of 6 What does this query return in SQLite?

    Read the code, then choose one answer.

    SELECT 'Asha' || ' ' || NULL || 'Rao' AS name;
    Show the answer to question 3

    Answer: NULL

    || returns NULL when either side is NULL, and once one step is NULL every later step is NULL too. concat_ws(' ', 'Asha', NULL, 'Rao') would return Asha Rao.

  4. Question 4 of 6 In MySQL 8.4 with the default SQL mode, what does SELECT 'Asha' || 'Rao' return?

    Choose one answer.

    Show the answer to question 4

    Answer: 0, because || means logical OR there

    Without the PIPES_AS_CONCAT mode, MySQL reads || as a deprecated spelling of OR. Both strings convert to the number 0, so the result is 0. Use CONCAT() or CONCAT_WS() in MySQL.

  5. Question 5 of 6 What does this query return in SQLite?

    Read the code, then choose one answer.

    SELECT substr('Bengaluru', -4) AS part;
    Show the answer to question 5

    Answer: luru

    A negative start counts from the end, and without a count substr() runs to the end of the string. PostgreSQL writes the same with right('Bengaluru', 4).

  6. Question 6 of 6 Which call joins the non-NULL values of three columns with a space between them in PostgreSQL, MySQL and SQLite alike?

    Choose one answer.

    Show the answer to question 6

    Answer: concat_ws(' ', a, b, c)

    concat_ws() takes the separator first and skips NULL arguments in all three engines. || returns NULL on any NULL (and means OR in MySQL), and MySQL's CONCAT() returns NULL when one argument is NULL.

Interview questions

Fresher · Why can LENGTH() return different numbers for the same Hindi word in MySQL and PostgreSQL?

Because the two functions count different things. MySQL’s LENGTH() returns the number of bytes, and in UTF-8 every Devanagari code point takes 3 bytes, so नमस्ते gives 18. PostgreSQL’s length() counts characters, meaning Unicode code points, and gives 6; MySQL’s equivalent is CHAR_LENGTH(), and both engines have octet_length() for bytes. A reader sees yet another number, 3 letters, because one written letter can combine several code points. The difference matters for validation and limits: a rule written with MySQL’s LENGTH() silently allows a third as many Hindi characters as English ones. Use the character count for user-facing limits, the byte count for storage, and cut text at spaces rather than at a position.

Key takeaways

  • length() counts code points and octet_length() bytes; a Devanagari code point takes 3 bytes, and one written letter can be several code points.
  • MySQL’s LENGTH() counts bytes; use CHAR_LENGTH() for characters.
  • substr() counts from 1, accepts a negative start in SQLite, and can cut a written letter in two.
  • || returns NULL if any part is NULL; concat_ws() skips NULLs in every engine, and in MySQL || means OR unless PIPES_AS_CONCAT is set.
  • SQLite’s upper() and lower() change ASCII letters only, and its printf() counts bytes unless you add !.
  • Text that looks the same can differ in code points; normalize it before storing or comparing.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.