2010-12-19 16 views
69

chcę wykonać jedną aktualizację surowego sql jak poniżej:Jak wykonać surowego aktualizacji sql z dynamicznym wiązaniu w szynach

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

Ten SQL zostanie wykonana przez ActiveRecord::Base.connection.execute, ale nie wiem jak przekazać dynamiczne wartości parametrów w metodzie.

Czy ktoś może mi pomóc?

+0

Dlaczego chcesz to zrobić przy użyciu surowego SQL, punktem ActiveRecord jest pomoc w uniknięciu tego ... – Andrew

+0

jeśli korzystam z AR, najpierw powinienem pobrać Model Object przez AR find method z polem id, a następnie wykonać operację aktualizacji. Tak więc z widoku operacji jeden UPDATE AR potrzebuje dwóch sqlsów z bazą danych; z drugiej strony nie jestem pewien, czy metoda aktualizacji AR używa dynamicznego powiązania. więc chcę używać raw sql z dynamicznym powiązanie dla jednej interakcji z db dla operacji aktualizacji, ale nie wiem, jak przekazać parametry, aby zastąpić? w sql przez AR. – ywenbo

+23

Jest wiele ważnych powodów, aby to zrobić. Po pierwsze, zapytanie może być zbyt skomplikowane, aby przełożyć się stosując regularną drogę Ruby ... po drugie, parametry może mieć znaków specjalnych, takich jak% lub cytatów, i jest to ból w dupie do ucieczki .. –

Odpowiedz

77

Nie wygląda na to, że interfejs Rails API udostępnia metody do wykonywania tego w sposób ogólny. Możesz spróbować uzyskać dostęp do podstawowego połączenia i użyć jego metod, np. MySQL:

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

Nie jestem pewien, czy istnieją inne konsekwencje dla robią to (połączenia otwarte, itp.) Śledziłbym kod Railsowy dla zwykłej aktualizacji, aby zobaczyć, co robi oprócz rzeczywistego zapytania.

Stosowanie przygotowanych zapytań pozwala zaoszczędzić trochę czasu w bazie danych, ale chyba że robisz to milion razy z rzędu, prawdopodobnie lepiej byłoby po prostu zbudować aktualizację z normalną podstawieniem Ruby, np.

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

lub przy użyciu ActiveRecord, tak jak powiedzieli komentatorzy.

+0

:-), dziękujemy za pomoc, masz rację, jeśli masz jakieś aktualizacje po wykryciu kodu szyny, daj mi znać. Jeśli naprawdę nie ma obnażeń, Twoja podana metoda będzie najlepsza dla mojej sprawy. Martwię się, że metoda aktualizacji ActiveRecord nie używa dynamicznego powiązania, jeśli tak naprawdę to jest złe. – ywenbo

+0

Wygląda na to, że powinieneś zadzwonić za darmo na wszelkie wyniki, które otrzymasz, ale nie sądzę, że dotyczy aktualizacji. Tak długo, jak zamykasz oświadczenie, tak jak pokazałem, powinieneś być w porządku. –

+0

Naprawdę doceniam twoją odpowiedź na moje problemy, dziękuję, do tej pory jest to jasne z twoją pomocą. – ywenbo

-12

W Rails 3.1, należy użyć interfejsu zapytań:

  • nowe (atrybuty)
  • utworzenia (atrybuty)
  • tworzyć (atrybuty)
  • znalezienia (id_or_array)
  • ! zniszcz (id_or_array)
  • destroy_all
  • delete (id_or_array)
  • delete_all
  • aktualizacyjne (IDS, aktualizacje)
  • update_all (aktualizacji)
  • istnieje?

update i update_all to operacja, której potrzebujesz.

Zobacz szczegóły tutaj: http://m.onkey.org/active-record-query-interface

4

Trzeba tylko użyć czegoś takiego:

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

To by rade. Korzystanie z ActiveRecord :: Base # wysłać metodę podniesienia zarzutu sanitize_sql_for_assignment sprawia, że ​​Ruby (przynajmniej w wersji 1.8.7) pominąć faktu, że sanitize_sql_for_assignment jest właściwie zabezpieczone metodą.

-8

Potrzebowałem używać surowego sql, ponieważ nie udało mi się uzyskać composite_primary_keys do działania z activerecord 2.3.8. Aby uzyskać dostęp do tabeli sqlserver 2000 ze złożonym kluczem podstawowym, wymagany był 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 

Jeśli dostępne jest lepsze rozwiązanie, proszę udostępnić.

+5

Wstawianie sql są możliwe tutaj! – mystdeim

+0

powinieneś również sprawdzić heredocs –

2

Sometime byłoby lepiej Nazwa Użyj klasy nadrzędnej zamiast nazwa tabeli:

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

Na przykład „osoba” klasa bazowa, podklasy (i tabele bazy danych) „Klient” i „Sprzedawca” Zamiast przy użyciu:

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

można użyć obiektu klasy bazowej w ten sposób:

person.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp) 
15

ActiveRecord::Base.connection ma quote sposób, że przyjmuje wartość łańcuch (ewentualnie obiektu kolumny). Więc można powiedzieć, to:

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

Uwaga, jeśli jesteś w migracji Rails lub obiektu ActiveRecord że można skrócić do:

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