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 2 – Filtering and sorting rows

LIKE and pattern matching

Match text in SQL with LIKE and its % and _ wildcards, search for a literal % or _ with ESCAPE, and choose between LIKE, ILIKE, GLOB and regular expressions.

  • Beginner
  • 15 minutes
  • Examples run with sql.js 1.14.2
  • By MySmartCoPilot

What you will learn

  • Match text with the LIKE wildcards % and _
  • Write patterns that match a literal % or _ by declaring an ESCAPE character
  • Choose between LIKE, ILIKE, GLOB and regular expressions for each engine

Before you start

On this page

= answers “is this text exactly that text?”. Real questions are often looser: names that start with “Sri”, product codes that end in “500G”, email addresses on a particular domain. LIKE compares a value with a pattern, a string in which two characters stand for “something here”.

The two wildcards

In a LIKE pattern:

  • % stands for any run of characters, including none at all;
  • _ stands for exactly one character;
  • every other character stands for itself.

The pattern has to describe the whole value, not just a part of it. 'Sri%' therefore means “starts with Sri”, '%Rao' means “ends with Rao”, '%sri%' means “contains sri somewhere”, and five underscores mean “exactly five characters long”.

Four patterns tried on six names SQL · wildcards.sql
CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO customers VALUES
  (1, 'Srinivas Rao'), (2, 'Sridevi Nair'), (3, 'Asrith Varma'),
  (4, 'Sunil Sri'),    (5, 'Srini'),        (6, 'Priya Iyer');

-- Each column is one pattern: 1 means the name matches it, 0 that it does not.
SELECT name,
       name LIKE 'Sri%'  AS starts_sri,
       name LIKE '%sri%' AS has_sri,
       name LIKE '%Rao'  AS ends_rao,
       name LIKE '_____' AS five_chars
FROM customers;

-- The same patterns filter rows in WHERE.
SELECT id, name
FROM customers
WHERE name LIKE 'Sri%';

Output

┌──────────────┬────────────┬─────────┬──────────┬────────────┐
│     name     │ starts_sri │ has_sri │ ends_rao │ five_chars │
├──────────────┼────────────┼─────────┼──────────┼────────────┤
│ Srinivas Rao │ 1          │ 1       │ 1        │ 0          │
│ Sridevi Nair │ 1          │ 1       │ 0        │ 0          │
│ Asrith Varma │ 0          │ 1       │ 0        │ 0          │
│ Sunil Sri    │ 0          │ 1       │ 0        │ 0          │
│ Srini        │ 1          │ 1       │ 0        │ 1          │
│ Priya Iyer   │ 0          │ 0       │ 0        │ 0          │
└──────────────┴────────────┴─────────┴──────────┴────────────┘
┌────┬──────────────┐
│ id │     name     │
├────┼──────────────┤
│ 1  │ Srinivas Rao │
│ 2  │ Sridevi Nair │
│ 5  │ Srini        │
└────┴──────────────┘

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

The first table puts each pattern in a column of its own, so you can see which names it matches (1) and which it does not (0). Two results deserve a second look. “Asrith Varma” contains sri in the middle of a word, and '%sri%' does not care about word boundaries. And '%sri%' matched names that start with a capital S: in SQLite, LIKE ignores the difference between capital and small letters, which is the subject of the next section. The second query uses a pattern the usual way, as a condition in WHERE.

A pattern can also be tried on a single value, without any table, which is a quick way to test one before you use it:

Counting characters with _ SQL · underscores.sql
-- Each _ stands for exactly one character; % for any number of them, even none.
SELECT 'Sri' LIKE 'S_i'  AS one,
       'Sri' LIKE 'S__i' AS two,
       'Sri' LIKE 'S%i'  AS pct;

Output

┌─────┬─────┬─────┐
│ one │ two │ pct │
├─────┼─────┼─────┤
│ 1   │ 0   │ 1   │
└─────┴─────┴─────┘

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

'S__i' asks for four characters, an S, any two and an i, so it does not match the three letters of Sri. % may stand for nothing at all, so 'S%i' matches.

Letter case: the engines disagree again

How LIKE treats capital and small letters is one of the differences you meet first when moving a query between engines:

  • SQLite ignores letter case in LIKE, but only for the 26 letters of the English alphabet. Accented and other non-English letters must match exactly.
  • PostgreSQL makes LIKE case-sensitive with its default collations. Its own operator ILIKE ignores case, using the case rules of the active locale. ILIKE is a PostgreSQL extension, not part of the SQL standard.
  • MySQL compares with the collation of the column, and its default collation is case-insensitive, so LIKE ignores case there unless the column or the pattern uses a case-sensitive or binary collation.
ASCII letters, accented letters and GLOB SQL · letter_case.sql
-- SQLite's LIKE ignores the case of the 26 ASCII letters only.
SELECT 'ECLAIR' LIKE 'eclair' AS ascii_letters,
       'ÉCLAIR' LIKE 'éclair' AS accented_letters,
       lower('ÉCLAIR')        AS lower_result;

-- GLOB never ignores case.
SELECT 'Pune' GLOB 'P*' AS capital_p,
       'Pune' GLOB 'p*' AS small_p;

Output

┌───────────────┬──────────────────┬──────────────┐
│ ascii_letters │ accented_letters │ lower_result │
├───────────────┼──────────────────┼──────────────┤
│ 1             │ 0                │ Éclair       │
└───────────────┴──────────────────┴──────────────┘
┌───────────┬─────────┐
│ capital_p │ small_p │
├───────────┼─────────┤
│ 1         │ 0       │
└───────────┴─────────┘

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

'ÉCLAIR' LIKE 'éclair' is false in SQLite because É is outside the ASCII range, and lower() has the same limit: it turned ÉCLAIR into Éclair, lowering only the English letters. GLOB, SQLite’s other pattern operator, never ignores case.

For a pattern that should ignore case in every engine, lower both sides yourself: lower(email) LIKE '%.in'. That works in PostgreSQL, MySQL and SQLite alike, within SQLite’s ASCII limit.

Searching for a real % or _

Product codes, file names and discount labels often contain _ or % themselves. Because those are wildcards, a pattern meant to find them can match far too much. This is the most common LIKE bug:

An underscore that is a wildcard, and one that is not SQL · escape.sql
CREATE TABLE products (code TEXT PRIMARY KEY, label TEXT NOT NULL);
INSERT INTO products VALUES
  ('TEA_500G',  'Assam tea, 50% off'),
  ('TEAX500G',  'Tea sampler'),
  ('TEA-1KG',   'Nilgiri tea, 5% cashback'),
  ('COFFEE_1K', 'Filter coffee');

-- _ means "any one character", so TEA_% also matches TEAX and TEA-.
-- With ESCAPE '!', the pattern TEA!_% looks for a real underscore.
SELECT code,
       code LIKE 'TEA_%'               AS any_character,
       code LIKE 'TEA!_%' ESCAPE '!'   AS real_underscore
FROM products;

-- Labels that contain a real percent sign.
SELECT label
FROM products
WHERE label LIKE '%!%%' ESCAPE '!';

Output

┌───────────┬───────────────┬─────────────────┐
│   code    │ any_character │ real_underscore │
├───────────┼───────────────┼─────────────────┤
│ COFFEE_1K │ 0             │ 0               │
│ TEA-1KG   │ 1             │ 0               │
│ TEAX500G  │ 1             │ 0               │
│ TEA_500G  │ 1             │ 1               │
└───────────┴───────────────┴─────────────────┘
┌──────────────────────────┐
│          label           │
├──────────────────────────┤
│ Assam tea, 50% off       │
│ Nilgiri tea, 5% cashback │
└──────────────────────────┘

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

'TEA_%' was meant to find codes that start with TEA_, but its _ matched the X of TEAX500G and the - of TEA-1KG as well. The fix is an escape character: the clause ESCAPE '!' says that in this pattern !_ means a real underscore and !% a real percent sign (and !! a real exclamation mark). The second query uses '%!%%': anything, then a real %, then anything.

The engines do not agree on what happens when you leave out ESCAPE:

  • PostgreSQL and MySQL treat a backslash as the escape character by default, so 'TEA\_%' works there.
  • SQLite has no default escape character, which is also what the SQL standard prescribes. Without an ESCAPE clause a backslash is an ordinary character, and 'TEA\_%' would look for a real backslash.
  • MySQL also treats a backslash inside any string literal as the start of an escape sequence, so the one-character string '\' has to be written '\\' there.

Writing the clause yourself with a character such as !, which needs no special treatment anywhere, gives the same result in all three engines.

GLOB in SQLite

SQLite has a second operator, GLOB, with the wildcards of file names on Unix systems: * for any run of characters, ? for exactly one, and square brackets for one character from a set, such as [0-9] for a digit or [A-Z] for a capital letter. GLOB always respects letter case.

Checking the shape of product codes with GLOB SQL · glob.sql
CREATE TABLE skus (code TEXT PRIMARY KEY);
INSERT INTO skus VALUES ('SKU-042'), ('SKU-42'), ('sku-042'), ('SKU-0A2'), ('SKU-1234');

-- GLOB: * any run of characters, ? exactly one, [0-9] one digit. Case counts.
SELECT code,
       code GLOB 'SKU-[0-9][0-9][0-9]' AS three_digits,
       code GLOB 'SKU-*'               AS any_suffix,
       code GLOB '???-???'             AS seven_chars
FROM skus;

Output

┌──────────┬──────────────┬────────────┬─────────────┐
│   code   │ three_digits │ any_suffix │ seven_chars │
├──────────┼──────────────┼────────────┼─────────────┤
│ SKU-042  │ 1            │ 1          │ 1           │
│ SKU-42   │ 0            │ 1          │ 0           │
│ sku-042  │ 0            │ 0          │ 1           │
│ SKU-0A2  │ 0            │ 1          │ 1           │
│ SKU-1234 │ 0            │ 1          │ 0           │
└──────────┴──────────────┴────────────┴─────────────┘

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

Only SKU-042 has exactly three digits after the dash. sku-042 fails every pattern that spells out SKU because GLOB respects case, while ???-??? is about length and position only. LIKE cannot say “a digit” at all, which is why GLOB is handy for checking the shape of codes. GLOB is SQLite’s own operator; in PostgreSQL and MySQL the same checks are written as regular expressions.

Missing values and invisible spaces

Two edge cases surprise people who clean data with LIKE:

A missing email and a trailing space SQL · nulls_and_spaces.sql
CREATE TABLE customers (id INTEGER PRIMARY KEY, email TEXT);
INSERT INTO customers VALUES
  (1, 'asha@mail.in'),
  (2, 'farhan@mail.com'),
  (3, NULL),             -- no email given
  (4, 'joseph@mail.in ') -- typed with a space at the end
;

SELECT count(*) AS all_rows FROM customers;
SELECT count(*) AS ends_in FROM customers WHERE email LIKE '%.in';
SELECT count(*) AS not_ends_in FROM customers WHERE email NOT LIKE '%.in';
SELECT count(*) AS ends_in_trimmed FROM customers WHERE trim(email) LIKE '%.in';

Output

┌──────────┐
│ all_rows │
├──────────┤
│ 4        │
└──────────┘
┌─────────┐
│ ends_in │
├─────────┤
│ 1       │
└─────────┘
┌─────────────┐
│ not_ends_in │
├─────────────┤
│ 2           │
└─────────────┘
┌─────────────────┐
│ ends_in_trimmed │
├─────────────────┤
│ 2               │
└─────────────────┘

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

There are four customers, but the LIKE and NOT LIKE counts add up to three. The customer without an email (NULL) is in neither: when the value is missing, the database cannot say whether it matches, so both conditions are unknown and WHERE drops the row. The next lesson is about exactly this. The other surprise is Joseph, whose address was saved with a space at the end: the pattern has to match the whole value, space included. trim() removes spaces at both ends before the comparison.

When another tool is the right one

Patterns that LIKE cannot express, such as “two to four digits, then a capital letter”, need regular expressions. They are written differently in each engine, and SQLite has none of its own:

PostgreSQL syntax run in SQLite SQL · other_dialects.sql
CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO customers VALUES (1, 'Srinivas Rao'), (2, 'Priya Iyer');

-- PostgreSQL's case-insensitive ILIKE is not SQLite (or MySQL) syntax.
SELECT name FROM customers WHERE name ILIKE 'sri%';

-- SQLite knows the REGEXP operator, but only a program that adds a regexp()
-- function can run it (the sqlite3 shell does; the browser runner does not).
SELECT name FROM customers WHERE name REGEXP '^Sri';

-- Plain LIKE runs everywhere.
SELECT name FROM customers WHERE name LIKE 'sri%';

Output (exit status 1)

┌──────────────┐
│     name     │
├──────────────┤
│ Srinivas Rao │
└──────────────┘

Printed as an error (standard error)

Parse error near line 5: near "ILIKE": syntax error
Parse error near line 9: no such function: REGEXP

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

SQLite rejects ILIKE with a syntax error. It accepts the REGEXP operator but has no regexp() function to run it unless the program using SQLite adds one. The sqlite3 command-line program adds one, so the REGEXP query works there; the browser runner here does not. The script carries on after each error, so the plain LIKE at the end still prints its row. Regular expressions get a lesson of their own in the text-search module.

Pattern matching at a glance

  • SQLite 3.49: LIKE ignores the case of the 26 English letters only; there is no escape character unless you declare one with ESCAPE; GLOB adds character classes such as [0-9]; regular expressions need a regexp() function that the application provides (the sqlite3 shell has one).
  • PostgreSQL 18: LIKE respects letter case and ILIKE ignores it; the backslash is the default escape character; SIMILAR TO and the regular-expression operators ~ and ~* cover character classes.
  • MySQL 8.4: LIKE follows the collation, which ignores case by default; the backslash is the default escape character unless the SQL mode NO_BACKSLASH_ESCAPES is on; regular expressions use REGEXP, also spelled RLIKE.

Patterns and indexes

An index keeps a column’s values sorted, like the names in a phone book. A pattern with a fixed start, such as 'Sri%', tells the database where in that order to start reading. A pattern that starts with a wildcard, such as '%vas', could match anywhere, so the database has to read every row. SQLite can show its plan:

Which LIKE patterns can use an index SQL · index_plan.sql
-- An index sorted without regard to case, which SQLite's LIKE can use.
CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE INDEX customers_by_name ON customers (name COLLATE NOCASE);

-- The last column, detail, says how SQLite will find the rows.
EXPLAIN QUERY PLAN SELECT id FROM customers WHERE name LIKE 'Sri%';
EXPLAIN QUERY PLAN SELECT id FROM customers WHERE name LIKE '%vas';

Output

┌────┬────────┬─────────┬─────────────────────────────────────────────────────────────────────────────┐
│ id │ parent │ notused │                                   detail                                    │
├────┼────────┼─────────┼─────────────────────────────────────────────────────────────────────────────┤
│ 2  │ 0      │ 156     │ SEARCH customers USING COVERING INDEX customers_by_name (name>? AND name<?) │
└────┴────────┴─────────┴─────────────────────────────────────────────────────────────────────────────┘
┌────┬────────┬─────────┬────────────────┐
│ id │ parent │ notused │     detail     │
├────┼────────┼─────────┼────────────────┤
│ 2  │ 0      │ 216     │ SCAN customers │
└────┴────────┴─────────┴────────────────┘

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

SEARCH … USING COVERING INDEX means SQLite would read only the part of the index between two values; SCAN customers means it would read the whole table. SQLite uses an index for LIKE only in particular cases: here the index is sorted without regard to case (COLLATE NOCASE), to match the case-insensitive LIKE, and the pattern does not start with a wildcard. PostgreSQL has a similar rule for its B-tree indexes: one can serve name LIKE 'Sri%' but not name LIKE '%vas', and in a database that does not use the C locale the index needs a special operator class for it. The performance module shows how to read these plans properly.

Regex Tester Try a regular expression on sample text before you put it in a PostgreSQL or MySQL query.

Key takeaways

  • % matches any run of characters and _ exactly one; a LIKE pattern has to match the whole value.
  • Letter case: SQLite’s LIKE ignores it for English letters only, PostgreSQL’s respects it (use ILIKE), MySQL’s follows the collation.
  • To find a real % or _, write an escape character with ESCAPE '!'; it behaves the same in all three engines.
  • NULL matches neither LIKE nor NOT LIKE, and a stray space at the end is part of the value.
  • SQLite’s GLOB adds character classes such as [0-9]; regular expressions differ by engine.

Exercise

Exercise · Easy · SQL

Find the .in email addresses for a newsletter

A shop wants to send its newsletter to customers with an email address on an Indian domain. The table customers has these columns:

  • id: the customer's number;
  • name: the customer's name;
  • email: the email address, or NULL when the customer gave none;
  • marketing_ok: 1 when the customer agreed to receive emails, 0 when they did not.

Write a query that returns the id and email of every customer whose address ends with .in, whatever mix of capital and small letters it is typed in (.in, .IN or .In), and who agreed to receive emails.

An address such as tina@mail.india.com contains .in but does not end with it, so it does not count. The order of the rows does not matter. Your query should also work in PostgreSQL, whose LIKE respects letter case: the last sample test switches SQLite's LIKE to respect letter case too.

Starter code · in_emails.sql

-- Customers who agreed to emails (marketing_ok = 1) and whose address
-- ends with .in, in any letter case.
SELECT id, email
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: finds .in at the end of the address in any letter case
    setup: |
      CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT, email TEXT, marketing_ok INTEGER);
      INSERT INTO customers VALUES
        (1, 'Asha',  'asha@mail.in',        1),
        (2, 'Ravi',  'RAVI@MAIL.IN',        1),
        (3, 'Meera', 'meera@college.ac.in', 1),
        (4, 'Tina',  'tina@mail.india.com', 1),
        (5, 'Sam',   'sam@mailin',          1),
        (6, 'Karan', 'karan@mail.co.In',    1);
    expected:
      columns: [id, email]
      rows:
        - [1, asha@mail.in]
        - [2, RAVI@MAIL.IN]
        - [3, meera@college.ac.in]
        - [6, karan@mail.co.In]
  - name: leaves out customers who did not agree and customers without an email
    setup: |
      CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT, email TEXT, marketing_ok INTEGER);
      INSERT INTO customers VALUES
        (1, 'Divya', 'divya@mail.in', 0),
        (2, 'Imran', NULL,            1),
        (3, 'Nisha', 'nisha@mail.in', 1),
        (4, 'Omar',  'OMAR@MAIL.IN',  0);
    expected:
      columns: [id, email]
      rows:
        - [3, nisha@mail.in]
  - name: returns no rows when no address ends with .in
    setup: |
      CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT, email TEXT, marketing_ok INTEGER);
      INSERT INTO customers VALUES
        (1, 'Leo',  'leo@mail.com',   1),
        (2, 'Mira', 'mira@india.com', 1);
    expected:
      columns: [id, email]
      rows: []
  - name: still works when LIKE respects letter case, as it does in PostgreSQL
    setup: |
      -- This setting makes SQLite's LIKE respect letter case for the rest of the test, like PostgreSQL's LIKE.
      PRAGMA case_sensitive_like = ON;
      CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT, email TEXT, marketing_ok INTEGER);
      INSERT INTO customers VALUES
        (1, 'Uma',    'uma@mail.in',    1),
        (2, 'Vikram', 'VIKRAM@MAIL.IN', 1),
        (3, 'Wasim',  'wasim@mail.In',  1),
        (4, 'Xavier', 'xavier@mail.IN', 0);
    expected:
      columns: [id, email]
      rows:
        - [1, uma@mail.in]
        - [2, VIKRAM@MAIL.IN]
        - [3, wasim@mail.In]
A hint

The pattern '%.in' means "anything, then .in at the very end"; the dot is an ordinary character in LIKE. Lowering the column first, lower(email) LIKE '%.in', ignores letter case in every engine. Join the second condition, marketing_ok = 1, with AND.

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

5 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 5 What does this query print?

    What does this program print? Choose one answer.

    -- Each _ stands for exactly one character; % for any number of them, even none.
    SELECT 'Sri' LIKE 'S_i'  AS one,
           'Sri' LIKE 'S__i' AS two,
           'Sri' LIKE 'S%i'  AS pct;
    Show the answer to question 1

    Answer: it prints

    ┌─────┬─────┬─────┐
    │ one │ two │ pct │
    ├─────┼─────┼─────┤
    │ 1   │ 0   │ 1   │
    └─────┴─────┴─────┘
    

    'S_i' is S, exactly one character, then i, which Sri is (1). 'S__i' needs two characters between S and i, so four in all, and Sri has three (0). % can stand for any number of characters, none included, so 'S%i' matches (1).

  2. Question 2 of 5 Which of these product codes does the SQLite condition code GLOB 'SKU-[0-9][0-9][0-9]' keep?

    Choose one answer.

    Show the answer to question 2

    Answer: SKU-042

    Each [0-9] stands for exactly one digit, so the pattern describes SKU, a dash and three digits. GLOB respects letter case, so the lower-case sku-042 fails; SKU-42 has only two digits, and SKU-0A2 has a letter where a digit must be.

  3. Question 3 of 5 In PostgreSQL, which operator matches a pattern while ignoring the difference between capital and small letters?

    Choose one answer.

    Show the answer to question 3

    Answer: ILIKE

    PostgreSQL's LIKE respects letter case and ILIKE ignores it, using the case rules of the active locale. ILIKE is a PostgreSQL extension: SQLite and MySQL report a syntax error. Of the three engines only SQLite has GLOB, and SIMILAR TO is a different pattern language, closer to regular expressions.

  4. Question 4 of 5 In SQLite, which conditions keep exactly the labels that contain a real percent sign, such as 50% off?

    Choose every answer that is right.

    Show the answer to question 4

    Answer:

    • label LIKE '%!%%' ESCAPE '!'
    • label LIKE '%\%%' ESCAPE '\'

    With an ESCAPE clause the character after the escape character is matched as itself; any character works, and ! behaves the same in every engine. Without ESCAPE, SQLite has no escape character, so '%\%%' looks for a backslash. '%%%' is three wildcards in a row and matches every label.

  5. Question 5 of 5 A customer's email is NULL. Which of these conditions keeps that customer's row?

    Choose one answer.

    Show the answer to question 5

    Answer: email IS NULL OR email NOT LIKE '%.in'

    A missing value cannot be said to match a pattern or not to match it, so LIKE, NOT LIKE and NOT (… LIKE …) are all unknown, and WHERE drops the row. Even '%', which matches any text, does not match NULL. Only an explicit IS NULL keeps the row.

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.