15

It would be appreciated explaining the internal functionality of SUM function in Oracle, when encountering null values:
The result of

select sum(null) from dual;
is null

But when a null value is in a sequence of values (like sum of a null-able column), the calculated value of null value will be 0

select sum(value) from
(
select case when mod(level , 2) = 0 then null else level end as value from dual
connect by level <= 10
)
is 25

This will be more interesting when seeing the result of

select (1 + null) from dual
is null

As any operation with null will result null (except is null operator).

==========================
Some update due to comments:

create table odd_table as select sum(null) as some_name from dual;

Will result:

create table ODD_TABLE
(
  some_name NUMBER
)

Why some_name column is of type number?

Mohsen Heydari
  • 7,256
  • 4
  • 31
  • 46
  • possible duplicate of [Why does TSQL operator + behave differently than aggregation function sum()](http://stackoverflow.com/questions/16196713/why-does-tsql-operator-behave-differently-than-aggregation-function-sum) –  Jul 07 '13 at 11:43
  • 3
    Aggregate functions ignore NULLs. Arithmetic calculations propagate NULLs - so if there is a NULL anywhere in a calculation, the result will be NULL. Share and enjoy. – Bob Jarvis - Слава Україні Jul 07 '13 at 14:19
  • 1
    This is not a question to a specific problem that can be solved. – Stefan Steinegger Jul 08 '13 at 06:23

7 Answers7

13

If you are looking for a rationale for this behaviour, then it is to be found in the ANSI SQL standards which dictate that aggregate operators ignore NULL values.

If you wanted to override that behaviour then you're free to:

Sum(Coalesce(<expression>,0))

... although it would make more sense with Sum() to ...

Coalesce(Sum(<expression>),0)

You might more meaningfully:

Avg(Coalesce(<expression>,0))

... or ...

Min(Coalesce(<expression,0))

Other ANSI aggregation quirks:

  1. Count() never returns null (or negative, of course)
  2. Selecting only aggregation functions without a Group By will always return a single row, even if there is no data from which to select.

So ...

Coalesce(Count(<expression>),0)

... is a waste of a good coalesce.

David Aldridge
  • 51,479
  • 8
  • 68
  • 96
  • `Sum(Coalesce(,0))` doesn't override this behavior. It only does more work to arrive at the same answer. – durette Jul 08 '21 at 15:50
  • It returns "0" for the sum() of nulls, instead of returning null. It is indeed more work, so I suggested `Coalesce(Sum(),0)` instead to achieve that aim. – David Aldridge Jul 10 '21 at 12:41
12

SQL does not treat NULL values as zeros when calculating SUM, it ignores them:

Returns the sum of all the values, or only the DISTINCT values, in the expression. Null values are ignored.

This makes a difference only in one case - when the sequence being totalled up does not contain numeric items, only NULLs: if at least one number is present, the result is going to be numeric.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • This is exactly the information I needed: SUM ignores NULLs unless the only values available are NULL. Thanks! – Baodad Feb 10 '15 at 17:33
  • seems contradictory when compared to SQL Server. SUM(null) in SQL server results 'Operand data type NULL is invalid for sum operator...' – Aditya May 09 '16 at 14:07
4

You're looking at this the wrong way around. SUM() operates on a column, and ignores nulls.

To quote from the documentation:

This function takes as an argument any numeric data type or any nonnumeric data type that can be implicitly converted to a numeric data type. The function returns the same data type as the numeric data type of the argument.

A NULL has no data-type, and so your first example must return null; as a NULL is not numeric.

Your second example sums the numeric values in the column. The sum of 0 + null + 1 + 2 is 3; the NULL simply means that a number does not exist here.

Your third example is not an operation on a column; remove the SUM() and the answer will be the same as nothingness + 1 is still nothingness. You can't cast a NULL to an empty number as you can with a string as there's no such thing as an empty number. It either exists or it doesn't.

Ben
  • 51,770
  • 36
  • 127
  • 149
  • 2
    Don't think your point `NULL has no data-type` is correct. In SQL Server it is treated as `int` when doing `SELECT NULL` but is invalid in the `SUM`. I don't have Oracle installed but on SQL Fiddle trying `create table test_table as select sum(null) as x from dual;` then `INSERT INTO test_table VALUES ('TEST')` gives `invalid number` so it seems to be treated as numeric. – Martin Smith Jul 07 '13 at 12:01
  • 1
    Actually it looks as though in Oracle the `NULL` literal is treated as [`varchar2(0)`](http://www.dbasupport.com/forums/showthread.php?8666-What-is-the-data-type-of-NULL) it is the `SUM` that outputs a result of numeric datatype. – Martin Smith Jul 07 '13 at 12:36
  • NULL does have a type in Oracle. http://sqlfiddle.com/#!4/4571d/13 – durette Jul 08 '21 at 16:33
  • I'm fairly sure that's because of how things like UNION and CASE are evaluated @durette; when NULL is type-checked normally it doesn't have a type due to it's non-existence: http://sqlfiddle.com/#!4/4571d/15 – Ben Jul 09 '21 at 09:15
4

Arithmetic aggregate functions ignore nulls.

  • SUM() ignores them
  • AVG() calculates the average as if the null rows didn't exist (nulls don't count in the total or the divisor)
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 2
    "*effectively counting null as zero*" is not correct as the example `select sum(null) from dual` shows. It is the same as with avg(): the rows are not taken into account at all. –  Jul 07 '13 at 11:28
  • @a_horse_with_no_name fine... i was trying to make it easy to understand :/ – Bohemian Jul 07 '13 at 11:39
2

As Bohemian has pointed out, both SUM and AVG exclude entries with NULL in them. Those entries do not go into the aggregate. If AVG treated NULL entries as zero, it would bias the result towards zero.

It may appear to the casual observer as though SUM is treating NULL entries as zero. It's really excluding them. If all the entries are excluded, the result is no value at all, which is NULL. Your example illustrates this.

Walter Mitty
  • 18,205
  • 2
  • 28
  • 58
  • Thanks, but i am still interested on how this have been implemented? Think that we want to write some function like SUM – Mohsen Heydari Jul 07 '13 at 12:05
  • 1
    Loop through all the values to be aggregated, skipping over any NULLS, and including all the non null values in the aggregate. If you skipped over everything, the result is null. (note: "non null values" is redundant.) – Walter Mitty Jul 07 '13 at 12:25
1

This is incorrect: The sum of 0 + null + 1 + 2 is 3; select 0 + null + 1 + 2 total from dual;

Result is null! Similar statements give result null if any operand is null.

Alex S
  • 94
  • 1
  • 1
0

Here's a solution if you want to sum and NOT ignore nulls.

This solution splits the records into two groups: nulls and non-nulls. NVL2(a, 1, NULL) does this by changing all the non-nulls to 1 so they sort together identically. It then sorts those two groups to put the null group first (if there is one), then sums just the first of the two groups. If there are no nulls, there will be no null group, so that first group will contain all the rows. If, instead, there is at least one null, then that first group will only contain those nulls, and the sum of those nulls will be null.

SELECT SUM(a) AS standards_compliant_sum,
       SUM(a) KEEP(DENSE_RANK FIRST ORDER BY NVL2(a, 1, NULL) DESC) AS sum_with_nulls
  FROM (SELECT 41   AS a FROM DUAL UNION ALL
        SELECT NULL AS a FROM DUAL UNION ALL
        SELECT 42   AS a FROM DUAL UNION ALL
        SELECT 43   AS a FROM DUAL);

You can optionally include NULLS FIRST to make it a little more clear about what's going on. If you're intentionally ordering for the sake of moving nulls around, I always recommend this for code clarity.

SELECT SUM(a) AS standards_compliant_sum,
       SUM(a) KEEP(DENSE_RANK FIRST ORDER BY NVL2(a, 1, NULL) DESC NULLS FIRST) AS sum_with_nulls
  FROM (SELECT 41   AS a FROM DUAL UNION ALL
        SELECT NULL AS a FROM DUAL UNION ALL
        SELECT 42   AS a FROM DUAL UNION ALL
        SELECT 43   AS a FROM DUAL);
durette
  • 353
  • 1
  • 12