2012-03-19 16 views
12

Właśnie zacząłem używać Sequel w naprawdę małej aplikacji Sinatra. Ponieważ mam tylko jedną tabelę DB, nie muszę używać modeli.Jak zaktualizować lub wstawić na Sequel dataset?

Chcę zaktualizować rekord, jeśli istnieje, lub wstawić nowy rekord, jeśli nie istnieje. I wymyślił następujące rozwiązanie:

rec = $nums.where(:number => n, :type => t) 
    if $nums.select(1).where(rec.exists) 
    rec.update(:counter => :counter + 1) 
    else 
    $nums.insert(:number => n, :counter => 1, :type => t) 
    end 

Gdzie $nums jest DB[:numbers] zestawu danych.

Uważam, że ten sposób nie jest najbardziej elegancką implementacją zachowania "zaktualizuj lub wstaw".

Jak należy to zrobić?

+0

http://stackoverflow.com/questions/3647454/increment-counter-or-insert-row-in-one-statement-in-sqlite – Reactormonk

Odpowiedz

17

Prawdopodobnie nie powinieneś sprawdzać przed aktualizacją/wstawieniem; ponieważ:

  1. To jest dodatkowe połączenie db.
  2. To mogłoby wprowadzić warunki wyścigu.

Co należy zrobić, zamiast jest sprawdzenie wartości zwracanej aktualizacji:

rec = $nums.where(:number => n, :type => t) 
if 1 != rec.update(:counter => :counter + 1) 
    $nums.insert(:number => n, :counter => 1, :type => t) 
end 
+0

jest to miłe rozwiązanie. Dzięki – Akarsh

+1

To rozwiązanie nadal wprowadza możliwość wyścigu. Jeśli dwa równoległe procesy/wątki wykonają aktualizację (wiersz 2), zanim jeden z nich dojdzie do wstawienia (linia 3), zostaną wstawione dwa rekordy. Rozważ użycie czegoś takiego jak mutex, db lock lub odpowiednia strategia transakcyjna. – Flexoid

+0

Flexoid: masz rację, a poniższe rozwiązanie - w zasadzie "wszystko do transakcji" jest poprawne. Nadal nie ma sensu kolejność poleceń "SELECT, UPDATE, INSERT", gdy "UPDATE + INSERT" jest wystarczające. (+ transakcja, oczywiście.) Interesująca rzecz w/transakcji: jeśli dwie transakcje równolegle wzrastają, ten sam licznik * nadal * napotyka problemy. – radiospiel

2

Uważam, że nie można mieć go o wiele czystszego niż to (chociaż niektóre bazy danych mają specyficzną składnię upsert, która może być supported by Sequel). Możesz po prostu owinąć to, co masz w osobnej metodzie i udawać, że nie istnieje. :)

Tylko kilka sugestii:

  • ująć wszystko w ramach transakcji.
  • Utwórz niepowtarzalny indeks na polach (number, type).
  • Nie używaj zmiennych globalnych.
1

można użyć upsert, oprócz tego, że nie pracuje obecnie aktualizację liczników. Mam nadzieję, że przyszła wersja będzie - pomysły mile widziane!

Powiązane problemy