2011-07-26 4 views
10

Rails의 원시 SQL에서 추상화 된 기능을 사용하면 시간이 많이 걸립니다. 열망 로딩Rails에서 조인을 사용하여 update_all 수행

UPDATE FROM tasks AS t 
LEFT JOIN projects as p 
ON t.project_id = p.id 
SET t.invoice_id = 7 
WHERE p.organization_id == 42 
AND t.invoice_id IS NULL 

어떻게 레일 3.0.1에서이 작업을 수행 할 수 있습니다 : MySQL의에서 나는이 작업을 수행 할 수 있습니까? 나는 다음을 모두 시도했다 :

Tasks.joins(:project).where('projects.organization_id' => 42, :invoice_id => nil).update_all(:invoice_id => 7) 

그리고 위의 모든 변형. 모두 오류가 발생했거나 아무것도 찾지 못했습니다.

그럼 scope를 사용하려 :

Task.scope :find => {:joins => :project, :conditions => ["projects.organization_id == ? AND invoice_id IS NULL", @organization.id] } do 
    Task.update_all :invoice_id => @invoice.id 
end 

이 하나가 나에게 오류 undefined method 'to_sym' for #<Hash:0x1065c6438>했다.

간단한 SQL 쿼리를 복제하기 위해 너무 많은 시간을 보냈습니다. 도와주세요!


편집 : 임시 나쁜 솔루션은 주위에 얻을 N + 1 :

task_ids = Task.select('tasks.id').joins(:project).where('projects.organization_id' => @organization.id, :invoice_id => nil).collect{|t| t.id} 
Task.update_all ['invoice_id = ?', @invoice.id], ["id in (#{task_ids.join(',')})"] 

답변

3

"에서 업데이트가"하지 표준 SQL, 그것은 직접적으로 지원되지 않는 경우가 놀라운 일이 아니다 그래서 활성 레코드. 그러나 Active Record는 지원하지 않는 무언가를해야 할 때 추상화를 우회하여 바로 SQL을 발행 할 수있는 방법을 제공합니다. 모델 내부 :

sql = "UPDATE FROM tasks AS t 
LEFT JOIN projects as p 
ON t.project_id = p.id 
SET t.invoice_id = 7 
WHERE p.organization_id == 42 
AND t.invoice_id IS NULL" 
connection.update_sql(sql) 

액티브 :: 자료도 할 수있는 "select_by_sql"방법을 가지고 당신의 표준이 아닌 선택 문은 일반 액티브 레코드 모델 인스턴스를 반환합니다.

+0

Thanks, Wayne. 가능하다면 SQL의 다른 맛은 이런 종류의 쿼리를 상당히 다르게 처리하기 때문에 추상적으로하고 싶습니다. 위의 예제는'update tasks, projects set ... '로 바뀔 수 있으며 여전히 문제가있을 것입니다. 너 더 생각이 있다면 나는 그것을 고맙게 생각할 것이다! – glortho

+0

@Jed, 그것은 비표준 SQL이기도합니다. 액티브 레코드가 그것을 어떻게 추상화할지 모르겠습니다. SQL로 할 수있는 일 중 일부는 CRUD 추상화와 잘 일치하지 않습니다. 이것은 특히. –

+0

흥미 롭습니다. 그래서 1 + n (비 열심히 로딩)은 추상화 계층에서 이것을 수행하는 유일한 방법입니다. 나는 '범위'가 어떻게 든 해결책을 제시하지 않는다고 믿을 수 없다. 일하는 내 두 번째 노력과 같은 것이 아니겠습니까? 이제는 1 + n으로 진행하지만 지속 가능한 솔루션은 아닙니다. 도와 주셔서 감사합니다 ... – glortho

0

나는 다음과 같은

UPDATE FROM tasks AS t 
LEFT JOIN projects as p 
ON t.project_id = p.id 
SET t.invoice_id = 7 
WHERE p.organization_id == 42 
AND t.invoice_id IS NULL 

다음 Arel 쿼리로 작성 될 수 있다고 믿는다

Tasks.include(:projects).where("projects.organization_id = ?", 42).where("tasks.invoice_id IS NULL").update_all("tasks.invoice_id = ?", 7) 

이렇게하면 작업 및 프로젝트 사이의 올바른 관계를 가지고 있다고 가정합니다.

2

이는 순수 루비/레일, 그것은 SQL로 태그해서는 안된다 -

당신이 얻을 수있는 유일한 SQL 정보는 다음과 같습니다 다른 구문 상응하는 대신 표준되지 않습니다 "에서 업데이트"에서 시작, 예를 들면 이것 (나는 어느 쪽도하지 않을 것이다. 그러나 어이) 나는 루비/선로를 사용하지 않는다.

UPDATE tasks t 
SET t.invoice_id=7 
WHERE 
t.invoice_id IS NULL 
AND 
(SELECT 
p.organization_id 
FROM tasks t2 
LEFT JOIN projects p 
ON t.project_id=p.id 
WHERE t2.id=t.id)=42 
3

나는 3.0.8과 ARel 2.0.10, 우리는 직접에서 업데이트를 생성 할 수있는 레일 @ 적어도 생각하지만,이 하위 쿼리로 가입 해결하여과 같은 결과를 얻을 수 있습니다 , 예. 즉, SQL에없는 레일에서 하위 쿼리를 해결하는 것, ourse을의

UPDATE "tasks" 
SET "invoice_id" = 7 
WHERE "invoice_id" IS NULL AND "project_id" IN (1,2,3); 
-- assuming projects 1,2,3 have organization_id = 42 

:

Task.where(:invoice_id=>nil). 
where(:project_id=>Project.where(:organization_id=>42).collect(&:id)). 
update_all(:invoice_id => 7) 

이 같은 SQL을 생성합니다.하위 선택 SQL의를 생성하려면 다음과 같이 조금 Arel에 혼합 할 수 있습니다 :

t = Task.arel_table 
p = Project.arel_table 
Task.where(:invoice_id=>nil). 
where(
    t[:project_id].in(
    p.where(
     p[:organization_id].eq(42) 
    ).project(p[:id]) 
) 
).update_all(:invoice_id => 7) 

이 같은 SQL 생성하는 :

UPDATE "tasks" 
SET "invoice_id" = 7 
WHERE "invoice_id" IS NULL 
AND "project_id" IN (
SELECT "projects"."id" FROM "projects" 
WHERE "projects"."organization_id" = 42 
); 

가이 일을 순수한 ARel 방법은,하지만를 UpdateManager 구문이 비공식적으로 문서화되지 않았습니다.

관련 문제