Search code examples
oraclenullsum

When I'm doing this query I'm getting NULL values. Why?


I have a table called fact_trip which has a column as fare_final and I want to find the difference between values.

SQL> desc fact_trip
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 TRIP_UUID                                 NOT NULL VARCHAR2(20)
 DATESTR                                            DATE
 PRODUCT_TYPE_NAME                                  VARCHAR2(20)
 CITY_ID                                            NUMBER
 DRIVER_UUID                                        VARCHAR2(50)
 IS_COMPLETED                                       VARCHAR2(10)
 ETA                                                NUMBER
 ATA                                                NUMBER
 UFP_FARE                                           NUMBER(4,2)
 FARE_FINAL                                         NUMBER(4,2)

So when I do this I'm getting NULL as output:

SQL>  select sum(fare_final) from fact_trip where to_char(datestr, 'W')=1 - (select sum(fare_final) from fact_trip where to_char(datestr, 'W')=2);

SUM(FARE_FINAL)
---------------

I even tried doing those select queries individually like this:

SQL> select sum(fare_final) from fact_trip where to_char(datestr, 'W')=1;

SUM(FARE_FINAL)
---------------
         1821.6

SQL> select sum(fare_final) from fact_trip where to_char(datestr, 'W')=2;

SUM(FARE_FINAL)
---------------
             67

Which is of course fetching result. But then when I run those queries for what I want a difference of them, it's showing NULL. Like I want difference of them (1821.6 - 67). Can anybody tell what's wrong in it? Thank You!


Solution

  • SQL is not a worksheet, where you may evaluate any expression. You cannot use an expression as a SQL command: you have to eitherselect an expression from something, or calculate it in PL/SQL block.

    You want to calculate the difference, so you assumed your code is: subquery - subquery. But because of described above this is not syntactically correct command, and parser tries to find a parse tree which is syntactically correct. And it actually finds one:

    select sum(fare_final)
    from fact_trip
    where to_char(datestr, 'W') = (
      1 - (
        select sum(fare_final)
        from fact_trip
        where to_char(datestr, 'W') = 2
      )
    )
    

    Of course, such week doesn't exist and you get null as a result.

    To turn your code into a correct command, the simplest way is to select the expression from a dual table with appropriate brackets to specify calculation precedence:

    select
      (select sum(fare_final) from fact_trip where to_char(datestr, 'W')='1')
      - (select sum(fare_final) from fact_trip where to_char(datestr, 'W')='2') as res
    from dual
    

    But more performant and set-based approach would be:

    select
      sum(
        case to_char(datestr, 'W')
          when '1' then 1
          when '2' then -1
        end * fare_final
      ) as res
    from fact_trip
    where to_char(datestr, 'W') in ('1', '2')
    

    Below is the sample code with results of all that was described above:

    select * from t
    
    ID | VAL
    -: | --:
     1 |   3
     2 |   6
     3 |   9
    
    select sum(val)
    from t
    where id = 1 - (
      select sum(val)
      from t
      where id = 2
    )
    
    | SUM(VAL) |
    | -------: |
    |     null |
    
    select
      (
    (select sum(val)
    from t
    where id = 1) - (
      select sum(val)
      from t
      where id = 2
    )
      ) as q
    from dual
    
    |  Q |
    | -: |
    | -3 |
    
    select
      sum(
        case id
         when 1 then 1
         when 2 then -1
        end * val
      ) as res
    from t
    where id in (1, 2)
    
    | RES |
    | --: |
    |  -3 |
    

    db<>fiddle here