skip to content

How do you correctly negate a compound predicate such as NOT (a = 1 AND b = 2)?

level: middleimportance: should knowfreq 52%

answer

  1. NOT grabs less than you think
  2. the connective has to change too
  3. a law named after a logician applies
  4. AND becomes OR when you push NOT inside
  5. parenthesize the group you negate

basics

~20 s

NOT binds tighter than AND and OR, so parenthesize whatever you negate. Negating a compound predicate flips the connective by De Morgan: NOT (a = 1 AND b = 2) equals a <> 1 OR b <> 2, and NOT (a = 1 OR b = 2) equals a <> 1 AND b <> 2.

solid answer

~50 s

Two rules do the work. First, precedence: `NOT` applies only to the predicate or parenthesized group immediately to its right, so `NOT a = 1 AND b = 2` means `(NOT a = 1) AND b = 2` — if you want the whole conjunction negated you must write the parentheses. Second, De Morgan: pushing `NOT` inside a group flips the connective and negates each operand, so `NOT (a = 1 AND b = 2)` becomes `a <> 1 OR b <> 2`, and `NOT (a = 1 OR b = 2)` becomes `a <> 1 AND b <> 2`. The usual mistake is flipping the operators but keeping the connective, producing `a <> 1 AND b <> 2` for the first case — that excludes far too many rows. One caveat: these equivalences are stated for non-null operands; when a column can be null the negated form still follows three-valued logic and rows with nulls are filtered out either way.

code

sql · 9 lines
sql
-- Intent: exclude documents that are BOTH archived and internal
SELECT doc_id
FROM documents
WHERE NOT (archived = 'Y' AND visibility = 'internal');

-- Equivalent De Morgan form (non-null columns)
SELECT doc_id
FROM documents
WHERE archived <> 'Y' OR visibility <> 'internal';

go deeper

for a junior

Remember that NOT applies only to the next predicate unless you add parentheses, and that <> is the standard inequality operator. Practice rewriting NOT (a = 1 AND b = 2) by hand.

for a middle

State both rules — NOT's precedence and De Morgan's laws — and perform the rewrite live, naming the wrong version (keeping AND) as the common error rather than a slip.

for a senior

Show how you validate a negated predicate: a counterexample row or a truth table, not inspection. Discuss choosing the form that mirrors the business rule and commenting predicates that encode exclusions.

for a principal

Treat negated filters as a known defect source in shared reporting and access-scoping logic: require explicit parentheses, review rewrites against representative data, and prefer positive formulations of rules where the domain allows.

## Rule one: NOT binds tightest Among SQL's logical connectives the binding order is `NOT`, then `AND`, then `OR`. `NOT` therefore takes exactly one operand: the predicate or parenthesized group directly to its right. Consider: ```sql WHERE NOT status = 'active' OR priority = 1 ``` This is `(NOT status = 'active') OR priority = 1`. Rows with `priority = 1` come back regardless of status. If the intent was "neither active nor priority one", the clause must be written `NOT (status = 'active' OR priority = 1)`. The practical rule: whenever `NOT` is followed by more than one predicate's worth of logic, put parentheses around what you are negating. There is no cost and it removes an entire class of misreadings. ## Rule two: De Morgan's laws Once the group is parenthesized, you often want to push the negation inside — to read it more easily, or to write the predicate in a form other people can scan. The transformation is mechanical: - `NOT (P AND Q)` is equivalent to `(NOT P) OR (NOT Q)` - `NOT (P OR Q)` is equivalent to `(NOT P) AND (NOT Q)` Applied to comparison predicates, `NOT (a = 1)` is `a <> 1`, `NOT (a > 5)` is `a <= 5`, `NOT (a <= 5)` is `a > 5`. So: ```sql -- "not both archived and internal" WHERE NOT (archived = 'Y' AND visibility = 'internal') -- equivalently WHERE archived <> 'Y' OR visibility <> 'internal' ``` ## The mistake everyone makes The negation is applied to the operators but not to the connective: ```sql -- WRONG rewrite of NOT (archived = 'Y' AND visibility = 'internal') WHERE archived <> 'Y' AND visibility <> 'internal' ``` That clause means "not archived *and* not internal", which is `NOT (archived = 'Y' OR visibility = 'internal')` — a strictly narrower condition. A document that is archived but public should be returned by the correct predicate and is excluded by the wrong one. Because both queries run and both return rows, the error shows up as "the list is missing things", usually long after the change. A quick sanity check is to enumerate a truth table over the two predicates. With P true and Q false, `NOT (P AND Q)` is true; `(NOT P) AND (NOT Q)` is false. One counterexample row is enough to prove the rewrite wrong, and constructing one is a good habit before shipping a negated filter. ## Negation and the inequality operator Standard SQL spells inequality `<>`. Many engines also accept `!=` as a synonym, but `<>` is the portable spelling. `NOT col = 'x'` and `col <> 'x'` express the same test for non-null operands; the second reads better and keeps `NOT` available for the cases that genuinely need a grouped negation. ## Nested negations De Morgan composes. `NOT (P AND (Q OR R))` becomes `(NOT P) OR NOT (Q OR R)`, then `(NOT P) OR ((NOT Q) AND (NOT R))`. Push the negation inward one layer at a time and parenthesize as you go; skipping steps is how sign errors appear. Double negation cancels: `NOT (NOT P)` is `P`. ## When to leave the NOT where it is Pushing the negation inward is not always an improvement. `NOT (status = 'active' AND region = 'EU')` may state the business rule more faithfully than its De Morgan twin, and a reader who knows the rule as an exclusion will find the outer `NOT` clearer. Choose the form that reads like the requirement, keep the parentheses explicit, and add a comment when the predicate encodes a subtle rule. ## The nullability caveat Everything above is stated for operands that are known to be non-null. When a column can hold null, both the original and the rewritten predicate follow SQL's three-valued logic, and a `WHERE` clause keeps only rows for which the predicate is true; rows where the comparison is neither true nor false are dropped by both forms. That is a separate subject with its own rules — the point here is simply that a De Morgan rewrite does not *introduce* a difference, and that you should not reason about negation on nullable columns as if it were two-valued. ## What interviewers listen for The expected answer names both rules — precedence of `NOT`, and De Morgan — performs the rewrite correctly on the spot, and identifies the classic wrong rewrite (`AND` kept instead of flipped to `OR`) as a misconception rather than a typo. Bonus points for validating a rewrite with a counterexample row rather than by staring at it.

  • What does WHERE NOT a = 1 AND b = 2 mean without any parentheses?
    `NOT` binds tighter than `AND`, so it applies only to the first comparison: the clause is `(a <> 1) AND (b = 2)`. Rows must have b equal to 2 and a different from 1. If the intent was to negate the whole conjunction, the query needs `NOT (a = 1 AND b = 2)`, which is a strictly broader condition.
  • How would you convince a reviewer that a negation rewrite is correct?
    Build a truth table over the component predicates and look for a row where the two forms disagree. For `NOT (P AND Q)` versus `(NOT P) AND (NOT Q)`, the case P true and Q false separates them immediately. In a live database the same check is a query over a handful of representative rows comparing both predicates side by side.

saying these in an interview costs you the question

  • Rewrites NOT (a = 1 AND b = 2) as a <> 1 AND b <> 2
  • Assumes NOT applies to everything after it in the clause
  • Thinks NOT col = 'x' and col <> 'x' differ for non-null operands
  • Pushes NOT inward across nested groups in one step without parentheses
  • Treats a negated predicate as obviously correct without testing a counterexample

context