Sortowanie modelu po wyliczonym atrybucie

Witajcie,

Jest jakis sprytny sposob na sortowanie rekordow z bazy, po kolumnie wyliczonej ?

Class human def debt outlay - revenue end end
Jak najprosciej zmodzic cos w stylu:

Human.find(:all, :order => debt) # wiem ze nie jest poprawnie synktaktycznie, chodzi o idee

Czyli zeby posortowal wedlug kolumny wyliczonej ?

Jesli sie nie da, to jak to zrobic w miare sensownie ?

Wiem ze moge zrobic:

@humans.sort! { |a,b| a.debt <=> b.debt }

Jest cos lepszego ?

jesli dobrze rozumiem to

SELECT (outlay - revenue) AS ile FROM AAA ORDER BY ile;

Coś takiego powinno być ok

szybki przykład ;]

[code=sql]SELECT (updated_at - created_at) AS roznica from users ORDER BY roznica;

 roznica     

00:17:20.12534
00:21:13.837671
00:23:10.383091
00:29:13.673877
01:17:48.168357
(5 rows)[/code]

[quote=gotar]jesli dobrze rozumiem to

SELECT (outlay - revenue) AS ile FROM AAA ORDER BY ile;

Coś takiego powinno być ok[/quote]
To by dzialalo, jednak mam metode zdefiniowana w modelu, ktora ta zaleznosc wylicza, i moje pytanie wlasnie bylo czy moge na tej metodzie bazowac, pomijajac :select w konstrukcji SQL’a do AR

Po pierwsze, czy wogole sie to da

Po drugie, czy Rails to jakos sensownie obsluzy ? Czy bedzie przy kazdej iteracj wykonywal metode wyliczeniowa “w kolko” ?

Pozdrowienia

A nie możesz tego wyliczeniowego zapytania przerzucić na bazę, one tez potrafią liczyć

Moge, ale jak mam relacje miedzy obiektami, to zapytanie do AR zaczyna wygladac nieczytelnie.

Dla mnie priorytet jest czytelnosc kodu, nawet jesli ma dzialac o te 0.2ms dluzej.

Lepszy program ladnie napisany ktory nie dziala, bo latwo go naprawic, kod ktory dziala i jest zle napisany, jak sie zepsuje to powodzenia … :slight_smile:

A tak na powaznie, to wyobraz sobie relacje przez has_many_through i zapytanie laczace kila tabel.

Byc moze jestem uparty, nie na tyle jednak zeby nie dac sie przekonac ze moje podejscie jest bledne.

Pozdrowienia

Niestety nie ma na to sprytnego sposobu. AR nie jest w stanie zbudować z tego sam SQLa. Jeśli pozostaniesz przy rozwiązaniu typu:

Human.all.sort_by(&:metoda_wyliczajaca)

to tak szybko jak przybędzie Ci rekordów w szukanej tabelce tak szybko stanie się to powolne. No chyba, że wcześniej będziesz używał jakiś filtrów i limitów, a dopiero na otrzymanej, względnie małej kolekcji zastosujesz sortowanie - wtedy powinno być ok.

Przerzuć to w jakiś sposób na bazę danych. Czy to napisanie SQLa, czy zapisanie wyliczonej wartości w kolumnie jest równie dobrym rozwiązaniem.

Sortowanie po stronie Rubiego będzie zawsze wolne i pamięciożerne.

Również składniałbym się do przerzucenia tego na bazę.
Jeśli chcesz sortować po takim polu to najoptymalniej byłoby utworzyć w tabeli bazy kolumnę debt i dodatkowo założyć na niej indeks.

Wtedy masz szybko działający i czysty kod.

Jedyny koszt to zwiększony czas zapisu do bazy, podczas którego będzie musiał być uaktualniony indeks oraz konieczność aktualizacji pola debt przy zmianie outlay lub revenue.

Rozwiązanie Human.all.sort_by(&:metoda_wyliczajaca) (czy jakiekolwiek podobne sortowanie w Ruby) jest najgorsze pod względem wydajnościowym - trzeba wyciągnąć wszystkie rekordy z bazy i je ręcznie posortować.

To jest dosyć istotny koszt. Oczywiście mniejszy niż narzut wydajnościowy przy sortowaniu po stronie Rubiego, ale mimo wszystko trzeba pamiętać, że trzeba o tym zawsze pamiętać kiedy modyfikuje się outlay lub revenue. Na szczęście w AR można do tego wykorzystać mechanizm śledzenia zmian w atrybutach (metoda “changes”), który powinien pozwolić załatwić sprawę “globalnie”. Niemniej jednak, jeśli baza może być modyfikowana przez innych klientów (tzn. za pomocą innych interfejsów niż Raislowy), to może okazać się, że dane w bazie są niespójne i wtedy jednak warto dodać odpowiedni mechanizm na poziomie BD.