Last active
August 29, 2015 14:24
-
-
Save ippeiukai/179c2e9442eae28ff44b to your computer and use it in GitHub Desktop.
Bulk update some rows with different values. (for Sequel)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module SequelBulkUpdate | |
class << self | |
# NOTE assumes the relation is simple | |
# | |
# @param relation [Sequel::Dataset] scope of update | |
# @param values [Array<Hash{Symbol=>_},#to_h>] values of each row required for updating | |
# @param where_columns [Array<Symbol>] columns used to identify the target row | |
# @param set_columns [Array<Symbol>] columns to update | |
def bulk_update(dataset, values, where_columns, set_columns) | |
_bulk_update(:update, dataset, values, where_columns, set_columns) | |
end | |
def bulk_update_sql(dataset, values, where_columns, set_columns) | |
_bulk_update(:update_sql, dataset, values, where_columns, set_columns) | |
end | |
private | |
def _bulk_update(update_method, dataset, values, where_columns, set_columns) | |
return if values.empty? | |
db = dataset.db | |
temporary_table_columns = [*where_columns, *set_columns] | |
temporary_table_dataset = values.each_with_index.inject(db.dataset) do |tmp_dataset, (record, i)| | |
values = record.to_h.values_at(*temporary_table_columns) | |
if i == 0 | |
values_with_alias = temporary_table_columns.zip(values).map do |col_name, value| | |
Sequel.as(value, col_name) | |
end | |
tmp_dataset.select(*values_with_alias) | |
else | |
tmp_dataset.union(db.dataset.select(*values), all: true, from_self: false) | |
end | |
end | |
if dataset.supports_modifying_joins? | |
temporary_table_name = dataset.unused_table_alias(:update_data) | |
join_cond = where_columns.map do |col| | |
{Sequel.qualify(temporary_table_name, col) => Sequel.qualify(dataset.first_source_table, col)} | |
end.inject(&:merge) | |
update_cols = set_columns.map do |col| | |
{col => Sequel.qualify(temporary_table_name, col)} | |
end.inject(&:merge) | |
dataset. | |
join(Sequel.as(temporary_table_dataset, temporary_table_name), join_cond). | |
public_send(update_method, update_cols) | |
else | |
# SQLite etc. | |
correlate_cond = where_columns.map do |col| | |
{col => Sequel.qualify(dataset.first_source_table, col)} | |
end.inject(&:merge) | |
update_cols = set_columns.map do |col| | |
{col => db.dataset.select(col).from(temporary_table_dataset).where(correlate_cond)} | |
end.inject(&:merge) | |
dataset. | |
where(db.dataset.from(temporary_table_dataset).where(correlate_cond).exists). | |
public_send(update_method, update_cols) | |
end | |
end | |
end | |
extend ActiveSupport::Concern | |
# @see ActiveRecordBulkUpdate.bulk_update | |
def bulk_update(*args, &block) | |
SequelBulkUpdate.bulk_update(self, *args, &block) | |
end | |
# @see ActiveRecordBulkUpdate.bulk_update_sql | |
def bulk_update_sql(*args, &block) | |
SequelBulkUpdate.bulk_update_sql(self, *args, &block) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Demo:
Special thanks to following posts:
http://stackoverflow.com/a/19033152/4212945
http://stackoverflow.com/a/21074659/4212945