Predicates — Learn LIKE, BETWEEN, ANY, and Compound EXISTS in Practice
AdvancedPredicates (Advanced)LIKE / BETWEENIS NULL / ANYMultiple EXISTSPostgreSQL5 questions
QUESTION 6
LIKE Predicate × EXISTS Predicate — Practical search combining pattern matching and existence checks
LIKE PredicateEXISTSPattern MatchingUser Search
Background

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'
Only prefix matches ('T%') can use an index: Infix and suffix matches cannot use a B-Tree index and require a full scan. For large datasets, use the pg_trgm extension + a GIN index or full-text search.
Problem

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.

Tables used
▸ users
user_idnameemailplan
1Taro Tanakatanaka@example.compremium
2Hanako Satosato@example.comstandard
3Ichiro Tamuratamura@example.comfree
4Jiro Yamadayamada@example.compremium
5Saburo Itoito@example.compremium
▸ orders
order_iduser_idstatus
1011completed
1022completed
1033completed
1043completed
1054cancelled
1065pending
Expected Output

Expected output (user_id ascending):

user_idnameemail
1Taro Tanakatanaka@example.com
3Ichiro Tamuratamura@example.com

Note: user 2 (Sato → starts with 'S'), user 4 (Yamada → starts with 'Y'), and user 5 (Ito) do not match LIKE 'T%' and are excluded.

QUESTION 7
BETWEEN Predicate × NOT EXISTS Predicate — Price-band filtering and detecting unsold products
BETWEENNOT EXISTSRange FilteringInventory & Sales Analysis
Background

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'
BETWEEN readability: The paired lower and upper bounds are visually clear and communicate intent well. However, date and timestamp types include only midnight at the start of the final day, so >= start AND < end+1 is safer for date ranges.
Problem

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.

Tables used
▸ products
product_idnamecategorypricestock
1Wireless MouseAccessories350080
2Mechanical KeyboardAccessories880045
3HDMI Cable 2mAccessories1200150
47-Port USB HubAccessories420030
5External SSD 1TBStorage980020
6HD WebcamAccessories650015
▸ orders
order_idproduct_idstatus
1014completed
1026pending
1031cancelled
Expected Output

Expected output (price ascending):

product_idnamepricestock
1Wireless Mouse350080
6HD Webcam650015

Note: product 4 (7-Port USB Hub) has completed order 101 → NOT EXISTS=FALSE and is excluded. Product 2: 8800 > 7000 / product 3: 1200 < 3000 → outside BETWEEN. Product 5 is in the Storage category.

QUESTION 8
IS NULL Predicate × IN Subquery — NULL-safe filtering and dynamic-list filtering
IS NULLIN SubqueriesNULL SafetyThree-Valued Logic
Background

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
The essence of three-valued logic: 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.
Problem

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.

Tables used
▸ orders
order_iduser_idamountstatusshipped_at
10118000completed2024-01-15
10225000processingNULL
103112000processingNULL
10433000completed2024-01-20
10549500processing2024-01-25
10637000processingNULL
▸ users
user_idnameplan
1Taro Tanakapremium
2Hanako Satopremium
3Ichiro Suzukistandard
4Jiro Yamadafree
Expected Output

Expected output (amount descending):

order_iduser_idamountstatus
103112000processing
10225000processing

Note: orders 101 and 104 are completed → excluded. Order 105 has shipped_at=2024-01-25 (non-NULL) → it does not match IS NULL and is excluded. Order 106 has user_id=3 (standard) → it is outside the IN list.

QUESTION 9
ANY / ALL Predicates — Comparing with some or all values (competitive price analysis)
ANY PredicateALL PredicateMulti-Value ComparisonCompetitive Analysis
Background

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)
The ALL + NULL trap: If an ALL list contains NULL, 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.
Problem

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.)

Tables used
▸ products (our products)
product_idnamecategoryprice
1Model A HeadphonesAudio12000
2Model B HeadphonesAudio8500
3Model C HeadphonesAudio15000
4Model D HeadphonesAudio6000
▸ competitor_prices (competitors)
comp_idnamecategoryprice
1Competitor X HeadphonesAudio9000
2Competitor Y HeadphonesAudio11000
3Competitor Z HeadphonesAudio13500
Expected Output

Expected output (< ANY, price ascending):

product_idnameprice
4Model D Headphones6000
2Model B Headphones8500
1Model A Headphones12000

Note: Model C (15000) is higher than the competitors' maximum of 13500, so it is not cheaper than any competitor and is excluded.

QUESTION 10
Compound Multiple EXISTS — Precise row selection with AND EXISTS + NOT EXISTS
Multiple EXISTSAND EXISTS + NOT EXISTSPractical SynthesisUser Behavior Analysis
Background

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'
)
The power of multiple EXISTS: Complex behavior patterns such as “a valuable user with completed orders but no cancelled orders” can be expressed simply by adding subqueries. Conditions that are difficult to express with JOIN + aggregation can be written intuitively by combining EXISTS predicates.
Problem

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.

Tables used
▸ users
user_idnameplan
1Taro Tanakapremium
2Hanako Satostandard
3Ichiro Suzukipremium
4Jiro Yamadafree
5Saburo Itostandard
▸ orders
order_iduser_idstatus
1011completed
1021cancelled
1032completed
1043completed
1053pending
1065pending
Expected Output

Expected output (user_id ascending):

user_idnameplan
2Hanako Satostandard
3Ichiro Suzukipremium

Note: user 1 has a completed order but also a cancelled order → excluded. User 4 is on the free plan → excluded by IN. User 5 has no completed order (only pending) → EXISTS=FALSE and is excluded.