Only return changed records from upsert_all

Has anyone found a way to make the Rails model upsert_all omit those records in the returning which were neither inserted nor actually changed in an update?

what we’re seeing is that returning will include all records updated, even if no attributes were actually changed.

we’re trying to bulk update a batch of records, and determine which of those were changed.

thus far, we’ve found a way to do this by passing Arel to the on_duplicate clause:

TestModel.upsert_all(
    ...
    on_duplicate: Arel.sql("#{TestModel.table_name}.foo = excluded.foo WHERE #{TestModel.table_name}.* IS DISTINCT FROM excluded.*")
)

which inserts the Arel SQL between the generated sql ....DO UPDATE SET and the ...RETURNING.

This behavior would depend on whether the underlying database supports it. Some DBs don’t even support the returning clause. I don’t think it’s possible for ActiveRecord to support this.

What DB are you using?

There might be a way to get it using raw SQL.

This is using Postgresql. We’ve implemented it via raw SQL already.