Zapytania do bazy w rails 3 (AREL)

Mam następujące pytanie:

Mam 3 modele i 3 odpowiadające im tabele w DB o następującej strukturze:

Department(id, name) # has_many :employees
Employee(id, first_name, last_name, department_id) #belongs_to :department; has_many :work_records
WorkRecord(id, time, employee_id, date, department_id) #belongs_to :employee

#wiem, że department_id w WorkRecord nie powinno być bo można spokojnie z joina z pracownikami wyciągnąć to, ale dla ułatwienia dodałem :slight_smile:

WorkRecord przechowuje liczbę godzin jaką pracownik przepracował danego dnia.

Chcę teraz otrzymać miesięczne zestawienie ile czasu pracownicy pracowali w miesiącu i mieć to posortowane po nazwisku pracowników.

Potrzebuję zatem zrobić kilka joinów. Wyszło mi takie coś:

[code=ruby]end_date = start_date = Date.today
department_id = 1

@work_records = WorkRecord.where(:department_id => department_id)
.where(“date >= ?”, start_date)
.where(“date <= ?”, end_date)
.select(“SUM(time) as time_sum”)
.select("*")
.group(:employee_id)
.order(“last_name ASC”)
.find(:all, :joins => “LEFT JOIN employees ON employees.id = work_records.employee_id”)[/code]
I teraz kilka pytań:

  1. Czy ktoś wie, dla czego to działa z SQLite a MySQL wywala syntax error near: “, * FROM work_records” ???
  2. Jak zrobić joina AREL’owego? Nigdzie nie mogę znaleźć przykładowej składni joinów z areal (ani w dokumentacji, ani w żadnych tutorialach)?
  3. Czy można to jakoś ładniej zrobić? :slight_smile:

Joins resemble SQL strongly:

users.join(photos).on(users[:id].eq(photos[:user_id]))

=> SELECT * FROM users INNER JOIN photos ON users.id = photos.user_id

2 where i selecty pewnie mozna by polaczyc, ale w sumie po co? A czemu nie działa? Niestety nie wiem.

Tu masz kilka Joinów:

No ok. To widziałem. Ale ja chcę osiągnąć LEFT JOIN …

Może to pomoże?

http://rdoc.info/github/rails/arel/master/frames

w pole do szukania daj join, jest tam coś o Inner, Outter i left, right …

To ja powiem co wymyśliłem na MySQL’a, aczkolwiek taki to potwór wyszedł, że masakra.
Otóż:

[code=ruby]@work_records = WorkRecord.find_by_sql("
SELECT *, last_name, first_name,
SUM(time) as time_sum, work_records.id as work_record_id
FROM work_records
LEFT JOIN employees ON work_records.employee_id = employees.id
WHERE work_records.date >= ‘#{start_date}’ AND work_records.date <= ‘#{end_date}’
GROUP BY work_records.employee_id ORDER BY employees.last_name ASC;")

@work_records.each { |wr| wr.time = wr.time_sum.to_i }[/code]
Takie coś działa, tylko trzeba później zmieniać sumę ze stringa la liczbę, bo niestety otrzymujemy Stringa.
Działać działa, ale jest paskudne. Szczególnie, że mój WorkTime w rzeczywistości ma około 15 atrybutów, z kótrych każdy musi być zsumowany. Jednym słowem wychodzi jakiś taki potwór na 40 linii…

Ma ktoś pomysł jak to zrobić bardziej Clean Code’owo ??

Jakby kogoś to interesowało, to częściowo sobie poradziłem.
Mianowicie LEFT JOIN zrobiłem tak jakby po staremu ale z nową metodą, czlyli

WorkRecord.select( itd...).joins("LEFT JOIN employees ON employees.id = work_records.employee_id").. itd

Czyli to co po staremu było w parametrze :joins metody find teraz dajemy do joins() i jest ok. zwraca to nam ActiveRecordRelation więc jest Arel’owo :slight_smile: pewnie da się to zorbić jeszcze ładniej, ale jak na razie mi się nie udało…

Natomiast mimo wszystko wartości obliczone przez funkcje agregującą SUM, czyli np. SUM(time) as time_sum,
w MySQL zwracane są jako String i trzeba je sobie samemu przekonwertować na odpowiednie typy. To jest trochę (a nawet bardzo denerwujące :slight_smile: ), ale nie udało mi się jeszcze znaleźć rozwiązania…

Dodatkowo odryłem, że jeśli mamy w migracjach decimale i nie podamy parametrów :precision i :scale, to w MySQL są one utworzone jako decimal(10,0), więc trzeba też na to uważać…