NOT IN flips TRUE and FALSE while UNKNOWN remains UNKNOWN.

highlighted = computed this step

NOT IN inverts truth

NOT IN flips TRUE to FALSE and FALSE to TRUE. UNKNOWN stays UNKNOWN.

invert TRUE and FALSE\text{invert TRUE and FALSE}

Read NOT IN output

After inversion, 1 row is TRUE.

NOT IN TRUE rows=1\text{NOT IN TRUE rows}=1

IN-list examples are tiny finite WHERE-style filters with SQL-style TRUE/FALSE/UNKNOWN behavior; subqueries, type coercion, optimizer behavior, indexes, dialect details, and product claims are out of scope.

NOT IN inverts truth: base rowsidcolor1red2blue3green4NULL5red IN-list evaluationsourcevaluevaluesmodeinTruthtruthkeptreason0red[red, blue]not_inTRUEFALSEnomatched literal1blue[red, blue]not_inTRUEFALSEnomatched literal2green[red, blue]not_inFALSETRUEyesno match3NULL[red, blue]not_inUNKNOWNUNKNOWNnotested NULL4red[red, blue]not_inTRUEFALSEnomatched literal IN-list output rowsidcolor3green IN-list factsfactvalueinputRowCount5listLength2outputRowCount1trueRows1falseRows3unknownRows1testedNullRows1nullListNoMatchRows0modenot_inrowOrdersource_order

UNKNOWN remains

The NULL row is still UNKNOWN after NOT IN, so it is not kept.

UNKNOWN unchanged\text{UNKNOWN unchanged}

Summary

NOT IN is truth inversion, not a separate matching rule.

truth inversion\text{truth inversion}