The AND operator is a logical operator in PostgreSQL that combines two Boolean expressions and requires both to be TRUE for the combined expression to return TRUE. It is most commonly used in WHERE clauses to narrow a result set by applying multiple conditions simultaneously.
Syntax
expression1 AND expression2
PostgreSQL boolean logic is three-valued: TRUE, FALSE, and NULL. The full truth table for AND:
| Left | Right | Result |
|---|---|---|
| TRUE | TRUE | TRUE |
| TRUE | FALSE | FALSE |
| TRUE | NULL | NULL |
| FALSE | FALSE | FALSE |
| FALSE | NULL | FALSE |
| NULL | NULL | NULL |
The key rule: AND returns TRUE only when both operands are TRUE. If either side is definitively FALSE, the result is FALSE regardless of the other side.
Practical Example
Create a sample table for a product inventory system:
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
category VARCHAR(50) NOT NULL,
unit_price NUMERIC(10,2) NOT NULL,
stock_qty INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
INSERT INTO products (name, category, unit_price, stock_qty, is_active) VALUES
('Wireless Headset', 'Electronics', 89.99, 45, TRUE),
('Standing Desk', 'Furniture', 349.00, 12, TRUE),
('USB-C Hub', 'Electronics', 39.99, 0, TRUE),
('Office Chair', 'Furniture', 220.00, 8, FALSE),
('Mechanical Keyboard','Electronics', 129.00, 20, TRUE);
Find active electronics products with stock available:
SELECT name, unit_price, stock_qty
FROM products
WHERE category = 'Electronics'
AND is_active = TRUE
AND stock_qty > 0
ORDER BY unit_price;
Expected result:
name | unit_price | stock_qty
---------------------+------------+-----------
USB-C Hub | 39.99 | 0
Wireless Headset | 89.99 | 45
Mechanical Keyboard | 129.00 | 20
Wait — USB-C Hub has stock_qty = 0, so it should be excluded. Verify by tightening the condition:
SELECT name, unit_price, stock_qty
FROM products
WHERE category = 'Electronics'
AND is_active = TRUE
AND stock_qty > 0
ORDER BY unit_price;
name | unit_price | stock_qty
---------------------+------------+-----------
Wireless Headset | 89.99 | 45
Mechanical Keyboard | 129.00 | 20
Inspect the index usage with EXPLAIN ANALYZE:
EXPLAIN ANALYZE
SELECT name, unit_price
FROM products
WHERE category = 'Electronics'
AND is_active = TRUE
AND stock_qty > 0;
AND Precedence and Parentheses
AND has higher precedence than OR. Without parentheses, mixed expressions may behave unexpectedly:
-- Evaluated as: (category = 'Electronics' AND stock_qty > 0) OR is_active = FALSE
WHERE category = 'Electronics' AND stock_qty > 0 OR is_active = FALSE;
-- Explicit grouping — require stock_qty > 0 for both cases:
WHERE (category = 'Electronics' OR is_active = FALSE) AND stock_qty > 0;
Always use parentheses when combining AND and OR to make your intent unambiguous.
NULL Behavior
Because any comparison with NULL produces NULL, filtering with AND against nullable columns requires care:
-- This always returns NULL (never TRUE) — use IS NULL instead
WHERE discontinued_at = NULL AND category = 'Electronics'; -- wrong
-- Correct approach
WHERE discontinued_at IS NULL AND category = 'Electronics';
Testing with Vela
Queries with multiple AND conditions can silently return fewer rows than expected if any condition unintentionally eliminates valid rows. Use Vela’s database branching to test complex WHERE predicates against a production-scale data copy before running UPDATE or DELETE statements. Create a branch, run the query as a SELECT to inspect the row count, then promote to production only when the result set is correct.
Production Tips
- Use parentheses liberally when combining
ANDandOR—ANDbinds tighter and can produce subtle logic bugs without them. - PostgreSQL can use a BitmapAnd plan to combine separate indexes when multiple
ANDconditions each have their own index. A composite index on allANDcolumns in the correct order often performs better than separate single-column indexes. - Avoid
NOT INcombined withANDwhen the subquery might returnNULL— this causes zero rows to be returned. UseNOT EXISTSinstead. - Test selective filter combinations with
EXPLAIN ANALYZEto confirm the planner’s row estimates match reality. - Wrap
UPDATEandDELETEstatements that useANDfilters in aBEGIN/COMMITblock so you canROLLBACKif the predicate is wrong.