NULL Semantics
Description
A table consists of a set of rows and each row contains a set of columns.
A column is associated with a data type and represents
a specific attribute of an entity (for example, age
is a column of an
entity called person
). Sometimes, the value of a column
specific to a row is not known at the time the row comes into existence.
In SQL
, such values are represnted as NULL
. This section details the
semantics of NULL
values handling in various operators, expressions and
other SQL
constructs.
- Null handling in comparison operators
- Null handling in Logical operators
- Null handling in Expressions
- Null handling in WHERE, HAVING and JOIN conditions
- Null handling in GROUP BY and DISTINCT
- Null handling in ORDER BY
- Null handling in UNION, INTERSECT, EXCEPT
- Null handling in EXISTS and NOT EXISTS subquery
- Null handling in IN and NOT IN subquery
The following illustrates the schema layout and data of a table named person
. The data contains NULL
values in
the age
column and this table will be used in various examples in the sections below.
TABLE: person
Id | Name | Age |
---|---|---|
100 | Joe | 30 |
200 | Marry | NULL |
300 | Mike | 18 |
400 | Fred | 50 |
500 | Albert | NULL |
600 | Michelle | 30 |
700 | Dan | 50 |
Comparision operators
Apache spark supports the standard comparison operators such as ‘>’, ‘>=’, ‘=’, ‘<’ and ‘<=’.
The result of these operators is unknown or NULL
when one of the operarands or both the operands are
unknown or NULL
. In order to compare the NULL
values for equality, Spark provides a null-safe
equal operator (‘<=>’), which returns False
when one of the operand is NULL
and returns ‘True when
both the operands are
NULL. The following table illustrates the behaviour of comparison operators when
one or both operands are
NULL`:
Left Operand | Right Operand | > | >= | = | < | <= | <=> |
---|---|---|---|---|---|---|---|
NULL | Any value | NULL | NULL | NULL | NULL | NULL | False |
Any value | NULL | NULL | NULL | NULL | NULL | NULL | False |
NULL | NULL | NULL | NULL | NULL | NULL | NULL | True |
Examples
Logical operators
Spark supports standard logical operators such as AND
, OR
and NOT
. These operators take Boolean
expressions
as the arguments and return a Boolean
value.
The following tables illustrate the behavior of logical opeators when one or both operands are NULL
.
Left Operand | Right Operand | OR | AND |
---|---|---|---|
True | NULL | True | NULL |
False | NULL | NULL | False |
NULL | True | True | NULL |
NULL | False | NULL | NULL |
NULL | NULL | NULL | NULL |
operand | NOT |
---|---|
NULL | NULL |
Examples
Expressions
The comparison operators and logical operators are treated as expressions in Spark. Other than these two kinds of expressions, Spark supports other form of expressions such as function expressions, cast expressions, etc. The expressions in Spark can be broadly classified as :
- Null in-tolerent expressions
- Expressions that can process
NULL
value operands- The result of these expressions depends on the expression itself.
Null in-tolerant expressions
Null in-tolerant expressions return NULL
when one or more arguments of
expression are NULL
and most of the expressions fall in this category.
Examples
Expressions that can process null value operands.
This class of expressions are designed to handle NULL
values. The result of the
expressions depends on the expression itself. As an example, function expression isnull
returns a true
on null input and false
on non null input where as function coalesce
returns the first non NULL
value in its list of operands. However, coalesce
returns
NULL
when all its operands are NULL
. Below is an incomplete list of expressions of this category.
- COALESCE
- NULLIF
- IFNULL
- NVL
- NVL2
- ISNAN
- NANVL
- ISNULL
- ISNOTNULL
- ATLEASTNNONNULLS
- IN
Examples
Builtin Aggregate Expressions
Aggregate functions compute a single result by processing a set of input rows. Below are
the rules of how NULL
values are handled by aggregate functions.
NULL
values are ignored from processing by all the aggregate functions.- Only exception to this rule is COUNT(*) function.
- Some aggregate functions return
NULL
when all input values areNULL
or the input data set is empty.
The list of these functions is:- MAX
- MIN
- SUM
- AVG
- EVERY
- ANY
- SOME
Examples
Condition expressions in WHERE, HAVING and JOIN clauses.
WHERE
, HAVING
operators filter rows based on the user specified condition.
A JOIN
operator is used to combine rows from two tables based on a join condition.
For all the three operators, a condition expression is a boolean expression and can return
True, False or Unknown (NULL)
. They are “satisfied” if the result of the condition is True
.
Examples
Aggregate operator (GROUP BY, DISTINCT)
As discussed in the previous section comparison operator,
two NULL
values are not equal. However, for the purpose of grouping and distinct processing, the two or more
values with NULL data
are grouped together into the same bucket. This behaviour is conformant with SQL
standard and with other enterprise database management systems.
Examples
Sort operator (ORDER BY Clause)
Spark SQL supports null ordering specification in ORDER BY
clause. Spark processes the ORDER BY
clause by
placing all the NULL
values at first or at last depending on the null ordering specification. By default, all
the NULL
values are placed at first.
Examples
Set operators (UNION, INTERSECT, EXCEPT)
NULL
values are compared in a null-safe manner for equality in the context of
set operations. That means when comparing rows, two NULL
values are considered
equal unlike the regular EqualTo
(=
) operator.
Examples
EXISTS/NOT EXISTS Subquery
In Spark, EXISTS and NOT EXISTS expressions are allowed inside a WHERE clause.
These are boolean expressions which return either TRUE
or
FALSE
. In other words, EXISTS is a membership condition and returns TRUE
when the subquery it refers to returns one or more rows. Similary, NOT EXISTS
is a non-membership condition and returns TRUE when no rows or zero rows are
returned from the subquery.
These two expressions are not affected by presence of NULL in the result of the subquery. They are normally faster because they can be converted to semijoins / anti-semijoins without special provisions for null awareness.
Examples
IN/NOT IN Subquery
In Spark, IN
and NOT IN
expressions are allowed inside a WHERE clause of
a query. Unlike the EXISTS
expression, IN
expression can return a TRUE
,
FALSE
or UNKNOWN (NULL)
value. Conceptually a IN
expression is semantically
equivalent to a set of equality condition separated by a disjunctive operator (OR
).
For example, c1 IN (1, 2, 3) is semantically equivalent to (C1 = 1 OR c1 = 2 OR c1 = 3)
.
As far as handling NULL
values are concerned, the semantics can be deduced from
the NULL
value handling in comparison operators(=
) and logical operators(OR
).
To summarize, below are the rules for computing the result of an IN
expression.
- TRUE is returned when the non-NULL value in question is found in the list
- FALSE is returned when the non-NULL value is not found in the list and the list does not contain NULL values
- UNKNOWN is returned when the value is
NULL
, or the non-NULL value is not found in the list and the list contains at least oneNULL
value
NOT IN always returns UNKNOWN when the list contains NULL
, regardless of the input value.
This is because IN returns UNKNOWN if the value is not in the list containing NULL
,
and because NOT UNKNOWN is again UNKNOWN.