LIKE Predicate × EXISTS Predicate — Practical search combining pattern matching and existence checks
The LIKE predicate checks whether a string matches a pattern. It has two wildcards: % (any string of zero or more characters) and _ (any single character). Combined with EXISTS, it enables practical compound filters such as “name matches a pattern × has a purchase history.”
-- % : any string of zero or more characters WHERE name LIKE 'T%' -- starts with 'T' (prefix match) — index usable WHERE name LIKE '%T%' -- contains 'T' (infix match) — index not usable -- _ : any single character WHERE code LIKE 'A_01' -- A?01 format (? is any one character) -- ILIKE: case-insensitive (PostgreSQL-specific) WHERE email ILIKE '%@example.com'
From the users table, retrieve users whose name starts with 'T' (LIKE 'T%') and who have at least one completed order. Return user_id, name, email, ordered by user_id ascending.
| user_id | name | plan | |
|---|---|---|---|
| 1 | Tanaka Taro | tanaka@example.com | premium |
| 2 | Sato Hanako | sato@example.com | standard |
| 3 | Tamura Ichiro | tamura@example.com | free |
| 4 | Yamada Jiro | yamada@example.com | premium |
| 5 | Ito Saburo | ito@example.com | premium |
| order_id | user_id | status |
|---|---|---|
| 101 | 1 | completed |
| 102 | 2 | completed |
| 103 | 3 | completed |
| 104 | 3 | completed |
| 105 | 4 | cancelled |
| 106 | 5 | pending |
| user_id | name | |
|---|---|---|
| 1 | Tanaka Taro | tanaka@example.com |
| 3 | Tamura Ichiro | tamura@example.com |
BETWEEN Predicate × NOT EXISTS Predicate — Price-band filtering and detecting unsold products
BETWEEN is a concise predicate for range conditions. a BETWEEN x AND y is equivalent to a >= x AND a <= y, a closed interval that includes both endpoints. Combined with NOT EXISTS, it enables inventory and sales-analysis queries such as “price band × no sales history.”
-- BETWEEN: a closed interval including both endpoints (x, y) WHERE price BETWEEN 3000 AND 7000 -- equivalent to >= 3000 AND <= 7000 -- NOT BETWEEN: outside the range WHERE price NOT BETWEEN 3000 AND 7000 -- equivalent to < 3000 OR > 7000 -- Caution for date ranges (timestamp type) WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31' -- for timestamp, this reaches only '2024-01-31 00:00:00' → orders later that day are missed -- correct: created_at >= '2024-01-01' AND created_at < '2024-02-01'
>= start AND < end+1 is safer for date ranges.From the products table, retrieve products in the 'Accessories' category with price from 3,000 to 7,000 (BETWEEN) that have no completed orders, using NOT EXISTS. Return product_id, name, price, stock, ordered by price ascending.
| product_id | name | category | price | stock |
|---|---|---|---|---|
| 1 | Wireless Mouse | Accessories | 3500 | 80 |
| 2 | Mechanical Keyboard | Accessories | 8800 | 45 |
| 3 | HDMI Cable 2m | Accessories | 1200 | 150 |
| 4 | 7-Port USB Hub | Accessories | 4200 | 30 |
| 5 | External SSD 1TB | Storage | 9800 | 20 |
| 6 | HD Webcam | Accessories | 6500 | 15 |
| order_id | product_id | status |
|---|---|---|
| 101 | 4 | completed |
| 102 | 6 | pending |
| 103 | 1 | cancelled |
| product_id | name | price | stock |
|---|---|---|---|
| 1 | Wireless Mouse | 3500 | 80 |
| 6 | HD Webcam | 6500 | 15 |
IS NULL Predicate × IN Subquery — NULL-safe filtering and dynamic-list filtering
In SQL's three-valued logic (TRUE / FALSE / UNKNOWN), every comparison with NULL returns UNKNOWN. In a WHERE clause, UNKNOWN excludes a row just like FALSE. To test whether a value is NULL, using IS NULL / IS NOT NULL is essential.
-- ✗ = NULL always produces UNKNOWN (never use it) WHERE shipped_at = NULL -- always UNKNOWN → all rows excluded (the opposite of the intent) WHERE shipped_at <> NULL -- always UNKNOWN → all rows excluded -- ✓ IS NULL / IS NOT NULL are the only correct forms WHERE shipped_at IS NULL -- keep only NULL rows (find unshipped orders) WHERE shipped_at IS NOT NULL -- keep only non-NULL rows (shipped orders) -- NULL propagates through expressions SELECT amount + discount_amount -- discount is NULL → the result is also NULL SELECT COALESCE(discount_amount, 0) -- replace NULL with a fallback value using COALESCE
NULL = NULL evaluates to UNKNOWN, not TRUE. NULL values are not considered equal to one another. In PostgreSQL, NULL IS NOT DISTINCT FROM NULL compares two NULLs as equal.From the orders table, retrieve orders where status is 'processing' and shipped_at is NULL (unshipped), limited to orders from users whose plan is 'premium'. Use an IN subquery. Return order_id, user_id, amount, status, ordered by amount descending.
| order_id | user_id | amount | status | shipped_at |
|---|---|---|---|---|
| 101 | 1 | 8000 | completed | 2024-01-15 |
| 102 | 2 | 5000 | processing | NULL |
| 103 | 1 | 12000 | processing | NULL |
| 104 | 3 | 3000 | completed | 2024-01-20 |
| 105 | 4 | 9500 | processing | 2024-01-25 |
| 106 | 3 | 7000 | processing | NULL |
| user_id | name | plan |
|---|---|---|
| 1 | Tanaka Taro | premium |
| 2 | Sato Hanako | premium |
| 3 | Ichiro Suzuki | standard |
| 4 | Yamada Jiro | free |
| order_id | user_id | amount | status |
|---|---|---|---|
| 103 | 1 | 12000 | processing |
| 102 | 2 | 5000 | processing |
ANY / ALL Predicates — Comparing with some or all values (competitive price analysis)
The ANY predicate (also written SOME) returns TRUE when the comparison is TRUE for at least one value from the subquery. The ALL predicate returns TRUE only when the comparison is TRUE for every value from the subquery.
-- ANY: TRUE if the comparison is TRUE for one value → equivalent to a chain of ORs WHERE price < ANY (SELECT price FROM competitor) -- ↑ equivalent: WHERE price < (SELECT MAX(price) FROM competitor) -- ALL: TRUE if the comparison is TRUE for every value → equivalent to a chain of ANDs WHERE price > ALL (SELECT price FROM competitor) -- ↑ equivalent: WHERE price > (SELECT MAX(price) FROM competitor) -- Special equivalences WHERE col = ANY (list) -- equivalent to = IN (list) WHERE col <> ALL (list) -- equivalent to = NOT IN (list) (NULL trap applies)
val <> NULL becomes UNKNOWN and the entire ALL result becomes FALSE. Since <> ALL (the equivalent of NOT IN) can exclude every row when NULL is present, prefer NOT EXISTS in production.From the products table (our products), retrieve products that are cheaper than at least one same-category competitor product in the competitor_prices table, using < ANY. Return product_id, name, price, ordered by price ascending. (The explanation tab also shows the ALL variant.)
| product_id | name | category | price |
|---|---|---|---|
| 1 | Model A Headphones | Audio | 12000 |
| 2 | Model B Headphones | Audio | 8500 |
| 3 | Model C Headphones | Audio | 15000 |
| 4 | Model D Headphones | Audio | 6000 |
| comp_id | name | category | price |
|---|---|---|---|
| 1 | Competitor X Headphones | Audio | 9000 |
| 2 | Competitor Y Headphones | Audio | 11000 |
| 3 | Competitor Z Headphones | Audio | 13500 |
| product_id | name | price |
|---|---|---|
| 4 | Model D Headphones | 6000 |
| 2 | Model B Headphones | 8500 |
| 1 | Model A Headphones | 12000 |
Compound Multiple EXISTS — Precise row selection with AND EXISTS + NOT EXISTS
Combining multiple EXISTS / NOT EXISTS predicates with AND enables precise row selection: a row satisfying condition A exists, while no row satisfying condition B exists. This is one of the most common patterns in practical behavior analysis and segment extraction.
-- Combine multiple EXISTS predicates with AND WHERE EXISTS ( -- ① a row satisfying condition A exists SELECT 1 FROM orders o1 WHERE o1.user_id = u.user_id AND o1.status = 'completed' ) AND NOT EXISTS ( -- ② no row satisfying condition B exists SELECT 1 FROM orders o2 WHERE o2.user_id = u.user_id AND o2.status = 'cancelled' )
From the users table, retrieve users on the 'premium' or 'standard' plan who have at least one completed order and no cancelled orders. Return user_id, name, plan, ordered by user_id ascending.
| user_id | name | plan |
|---|---|---|
| 1 | Tanaka Taro | premium |
| 2 | Sato Hanako | standard |
| 3 | Ichiro Suzuki | premium |
| 4 | Yamada Jiro | free |
| 5 | Ito Saburo | standard |
| order_id | user_id | status |
|---|---|---|
| 101 | 1 | completed |
| 102 | 1 | cancelled |
| 103 | 2 | completed |
| 104 | 3 | completed |
| 105 | 3 | pending |
| 106 | 5 | pending |
| user_id | name | plan |
|---|---|---|
| 2 | Sato Hanako | standard |
| 3 | Ichiro Suzuki | premium |