简体   繁体   中英

Is there an SQL to show only the value that got changed from the previous row for all columns?

I have a history table where it has all the values and if the value changed then it will be a new row. This is the example

name    mod_date                create_user_id  is_active   other_column
name1   2020-01-06 22:06:58+00  1               1           value1
name1   2020-01-06 22:07:01+00  2               1           value2
name2   2020-01-06 22:07:27+00  1               1           value2

Then after a query I want the result to be like this

name    mod_date                create_user_id is_active other_column
name1   2020-01-06 22:06:58+00  1              1         value1
        2020-01-06 22:07:01+00  2                        value2
name2   2020-01-06 22:07:27+00  1       

The point I'm trying to make is it's easier to check which value got changed after a timestamp. The first row will always be there as it's default. Then the next row only shows create_user_id and other_column as it's changed from 1 to 2 and value1 to value2

is_active would just be empty as it's never changed

I read about lag and partition but it seems like it only works for one column as I want to check every column

This is my example

select name
from (select h.*,
    lag(name) over(partition by h.id order by h.mod_date) lag_name
from history h
) h
where name <> lag_name;

If you don't have too many columns then you can use lag separately for previous value of each column. you can also use alias for window as you will be running this for same window in order to make query easy to read.

select name
from (select h.*,
    lag(name) over main_window as lag_name,
    lag(create_user_id) over main_window as prev_user_id,
    lag(is_active) over main_window as prev_is_active,
    lag(other_column) over main_window as prev_other_column
from history h
window main_window as (partition by h.id order by h.mod_date)
) h
where name <> lag_name
or create_user_id <> prev_user_id
or is_active <> prev_is_active
or other_column <> prev_other_column

For each column, use an expression like

CASE WHEN lag(col) OVER (ORDER BY mod_date) IS DISTINCT FROM col THEN col END

That will produce NULL if the column value is the same for neighboring rows.

You are going to have to list all the columns, but I think the simplest expression is probably:

select nullif(name, lag(name) over (order by mod_date)) as name,
       mod_date,
       nullif(create_user_id, lag(create_user_id) over (order by mod_date)) as create_user_id,
       nullif(is_active, lag(is_active) over (order by mod_date)) as is_active,
       nullif(other_column, lag(name) over (order by mod_date)) as other_column
from t
order by mod_date;

You can construct the query for any audit table using information_schema.columns to get all the columns in the table.

This assumes that the column values are not NULL -- and if they are, then your results are ambiguous anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM