Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fill null values with last non-null amount - Oracle SQL

Tags:

sql

oracle

I have a table that has 4 columns: Item, Year, Month, Amount. Some of the values for Amount are null and when that happens I want to fill those values in with the previous Amount value that is not null. I can easily do this with the LAG function when there is only one null value but when there are multiple in a row I am not sure how to approach it. Below is an example of what the table might look like with an added column for what I want to add in my query:

Item | Year | Month | Amount | New_Amount
AAA  | 2013 | 01    | 100    | 100
AAA  | 2013 | 02    |        | 100
AAA  | 2013 | 03    | 150    | 150
AAA  | 2013 | 04    | 125    | 125
AAA  | 2013 | 05    |        | 125
AAA  | 2013 | 06    |        | 125
AAA  | 2013 | 07    |        | 125
AAA  | 2013 | 08    | 175    | 175

I had two ideas which I can't seem to get to work to produce what I want. First I was going to use LAG but then I noticed when there are multiple null values in a row it won't satisfy that. Next I was going to use FIRST_VALUE but that wouldn't help in this situation where there is a null followed by values followed by more nulls. Is there a way to use FIRST_VALUE or another similar function to retrieve the last non-null value?

like image 495
user1723699 Avatar asked Nov 17 '14 16:11

user1723699


People also ask

How fill all NULL values in SQL?

ISNULL Function in SQL Server To use this function, all you need to do is pass the column name in the first parameter and in the second parameter pass the value with which you want to replace the null value. So, now all the null values are replaced with No Name in the Name column.

How do I fill NULL values with 0 in SQL?

Use IFNULL or COALESCE() function in order to convert MySQL NULL to 0. Insert some records in the table using insert command. Display all records from the table using select statement.

How do you use nulls last?

If the null ordering is not specified then the handling of the null values is: - NULLS LAST if the sort is ASC - NULLS FIRST if the sort is DESC - If neither ascending nor descending order is specified, and the null ordering is also not specified, then both defaults are used and thus the order will be ascending with ...

Can we compare NULL with NULL in Oracle?

Because null represents a lack of data, a null cannot be equal or unequal to any value or to another null. However, Oracle considers two nulls to be equal when evaluating a DECODE function.


3 Answers

last_value with IGNORE NULLS works fine in Oracle 10g:

select item, year, month, amount, 
       last_value(amount ignore nulls) 
         over(partition by item 
              order by year, month 
              rows between unbounded preceding and 1 preceding) from tab;

rows between unbounded preceding and 1 preceding sets the window for analytic function.

In this case Oracle is searching for LAST_VALUE inside the group defined in PARTITION BY (the same item) from the begining (UNBOUNDED PRECEDING) until current row - 1 (1 PRECEDING)

It's a common replacement for LEAD/LAG with IGNORE NULLS in Oracle 10g

However, if you're using Oracle 11g you can use LAG from the Gordon Linoff's answer (there is a small typo with "ignore nulls")

like image 154
Multisync Avatar answered Sep 30 '22 00:09

Multisync


the answers are quite bad:

Item | Year | Month | Amount | New_Amount
AAA  | 2013 | 01    | 100    | null
AAA  | 2013 | 02    |        | 100
AAA  | 2013 | 03    | 150    | 100
AAA  | 2013 | 04    | 125    | 150
AAA  | 2013 | 05    |        | 125
AAA  | 2013 | 06    |        | 125
AAA  | 2013 | 07    |        | 125
AAA  | 2013 | 08    | 175    | 125

is a quite bad result :)

--

select item, year, month, amount, 
  last_value(amount ignore nulls) 
over(partition by item
  order by year, month
  rows between unbounded preceding and CURRENT ROW) from tab;

is better

like image 25
Pavel Avatar answered Sep 30 '22 02:09

Pavel


Here is an approach. Count the number of non-null values before a given row. Then use this as a group for a window function:

select t.item, t.year, t.month, t.amount,
       max(t.amount) over (partition by t.item, grp) as new_amount
from (select t.*,
             count(Amount) over (Partition by item order by year, month) as grp
      from table t
     ) t;

In Oracle version 11+, you can use ignore nulls for lag() and lead():

select t.item, t.year, t.month, t.amount,
       lag(t.amount ignore nulls) over (partition by t.item order by year, month) as new_amount
from table t
like image 30
Gordon Linoff Avatar answered Sep 30 '22 00:09

Gordon Linoff