Как выполнить сырое обновление sql с динамическим связыванием в рельсах - PullRequest
84 голосов
/ 19 декабря 2010

Я хочу выполнить одно обновление raw sql, как показано ниже:

update table set f1=? where f2=? and f3=?

Этот SQL будет выполнен ActiveRecord::Base.connection.execute, но я не знаю, как передать динамические значения параметров в метод.

Может ли кто-нибудь помочь мне?

Ответы [ 7 ]

93 голосов
/ 19 декабря 2010

Это не похоже на то, что Rails API предоставляет методы для общего применения. Вы можете попробовать получить доступ к базовому соединению и использовать его методы, например, для MySQL:

st = ActiveRecord::Base.connection.raw_connection.prepare("update table set f1=? where f2=? and f3=?")
st.execute(f1, f2, f3)
st.close

Я не уверен, есть ли другие последствия для этого (соединения остаются открытыми и т. Д.). Я бы отслеживал код Rails для нормального обновления, чтобы увидеть, что он делает, кроме фактического запроса.

Использование подготовленных запросов может сэкономить вам немного времени в базе данных, но если вы не делаете это миллион раз подряд, вам, вероятно, будет лучше просто создать обновление с обычной заменой Ruby, например

ActiveRecord::Base.connection.execute("update table set f1=#{ActiveRecord::Base.sanitize(f1)}")

или используя ActiveRecord, как сказали комментаторы.

27 голосов
/ 02 июля 2014

ActiveRecord::Base.connection имеет метод quote, который принимает строковое значение (и, необязательно, объект столбца). Таким образом, вы можете сказать это:

ActiveRecord::Base.connection.execute(<<-EOQ)
  UPDATE  foo
  SET     bar = #{ActiveRecord::Base.connection.quote(baz)}
EOQ

Обратите внимание, что если вы находитесь в миграции на Rails или объекте ActiveRecord, вы можете сократить его до:

connection.execute(<<-EOQ)
  UPDATE  foo
  SET     bar = #{connection.quote(baz)}
EOQ

ОБНОВЛЕНИЕ: Как указывает @kolen, вместо этого следует использовать exec_update. Это будет обрабатывать цитаты для вас, а также избежать утечки памяти. Подпись работает немного по-другому, хотя:

connection.exec_update(<<-EOQ, "SQL", [[nil, baz]])
  UPDATE  foo
  SET     bar = $1
EOQ

Здесь последний параметр - это массив кортежей, представляющих параметры связывания. В каждом кортеже первая запись - это тип столбца, а вторая - значение. Вы можете указать nil для типа столбца, и Rails, как правило, будет делать все правильно.

Есть также exec_query, exec_insert и exec_delete, в зависимости от того, что вам нужно.

4 голосов
/ 15 мая 2012

Вы должны просто использовать что-то вроде:

YourModel.update_all(
  ActiveRecord::Base.send(:sanitize_sql_for_assignment, {:value => "'wow'"})
)

Это бы сработало.Использование ActiveRecord :: Base # send для вызова sanitize_sql_for_assignment заставляет Ruby (как минимум версию 1.8.7) пропустить тот факт, что sanitize_sql_for_assignment на самом деле защищенный метод.

2 голосов
/ 30 ноября 2013

Иногда лучше использовать имя родительского класса вместо имени таблицы:

# Refers to the current class
self.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)

Например, базовый класс "Person", подклассы (и таблицы базы данных) "Client" и "Seller". Вместо этого используйте:

Client.where(self.class.primary_key => id).update_all(created _at: timestamp)
Seller.where(self.class.primary_key => id).update_all(created _at: timestamp)

Вы можете использовать объект базового класса следующим образом:

person.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
1 голос
/ 27 февраля 2019

Зачем использовать для этого необработанный SQL?

Если у вас есть модель для него , используйте where:

f1 = 'foo'
f2 = 'bar'
f3 = 'buzz'
YourModel.where('f1 = ? and f2 = ?', f1, f2).each do |ym|
  # or where(f1: f1, f2: f2).each do (...)
  ym.update(f3: f3) 
end

Если вы неДля него не существует модели (только таблица), вы можете создать файл и модель, которые будут наследоваться от ActiveRecord::Base

class YourTable < ActiveRecord::Base
  self.table_name = 'your_table' # specify explicitly if needed
end

и снова использовать where так же, как указано выше:

0 голосов
/ 25 апреля 2013

Мне нужно было использовать raw sql, потому что у меня не получилось заставить Combo_primary_keys функционировать с activerecord 2.3.8.Таким образом, чтобы получить доступ к таблице sqlserver 2000 с помощью составного первичного ключа, необходимо использовать raw sql.

sql = "update [db].[dbo].[#{Contacts.table_name}] " +
      "set [COLUMN] = 0 " +
      "where [CLIENT_ID] = '#{contact.CLIENT_ID}' and CONTACT_ID = '#{contact.CONTACT_ID}'"
st = ActiveRecord::Base.connection.raw_connection.prepare(sql)
st.execute

Если доступно лучшее решение, поделитесь.

0 голосов
/ 11 ноября 2011

В Rails 3.1 вы должны использовать интерфейс запросов:

  • new (атрибуты)
  • create (атрибуты)
  • create! (Атрибуты)
  • find (id_or_array)
  • destroy (id_or_array)
  • destroy_all
  • delete (id_or_array)
  • delete_all
  • update (идентификаторы, обновления)
  • update_all (обновления)
  • существует?

update и update_all - это операция, которая вам нужна.

Подробнее здесь: http://m.onkey.org/active-record-query-interface

...