2011-09-15 26 views
5

tôi nhận được truy vấn SQL rất xấu xí với Rails mã như được liệt kê dưới đây:Giảm sử dụng LEFT JOIN trong các truy vấn với bao gồm

Facility.includes(:type, :owner_building, :delegated_building, keeper_building, :owner_user, :keeper_user).order('users.name ASC').all 

Nó tạo:

SELECT `facilities`.`id` AS t0_r0, `facilities`.`name` AS t0_r1, `facilities`.`brand` AS t0_r2, `facilities`.`desc` AS t0_r3, `facilities`.`type_id` AS t0_r4, `facilities`.`owner_building_id` AS t0_r5, `facilities`.`keeper_building_id` AS t0_r6, `facilities`.`delegated_building_id` AS t0_r7, `facilities`.`owner_user_id` AS t0_r8, `facilities`.`keeper_user_id` AS t0_r9, `buildings`.`id` AS t1_r0, `buildings`.`name` AS t1_r1, `buildings`.`address` AS t1_r2, `buildings`.`created_at` AS t1_r3, `buildings`.`updated_at` AS t1_r4, `buildings`.`comments` AS t1_r5, `delegated_buildings_facilities`.`id` AS t2_r0, `delegated_buildings_facilities`.`name` AS t2_r1, `delegated_buildings_facilities`.`address` AS t2_r2, `delegated_buildings_facilities`.`created_at` AS t2_r3, `delegated_buildings_facilities`.`updated_at` AS t2_r4, `delegated_buildings_facilities`.`comments` AS t2_r5, `keeper_buildings_facilities`.`id` AS t3_r0, `keeper_buildings_facilities`.`name` AS t3_r1, `keeper_buildings_facilities`.`address` AS t3_r2, `keeper_buildings_facilities`.`created_at` AS t3_r3, `keeper_buildings_facilities`.`updated_at` AS t3_r4, `keeper_buildings_facilities`.`comments` AS t3_r5, `users`.`id` AS t4_r0, `users`.`company_id` AS t4_r1, `users`.`building_id` AS t4_r2, `users`.`login` AS t4_r3, `users`.`name` AS t4_r4, `users`.`role` AS t4_r5, `users`.`email` AS t4_r6, `users`.`comments` AS t4_r7, `users`.`crypted_password` AS t4_r8, `users`.`password_salt` AS t4_r9, `users`.`persistence_token` AS t4_r10, `users`.`perishable_token` AS t4_r11, `users`.`login_count` AS t4_r12, `users`.`failed_login_count` AS t4_r13, `users`.`last_request_at` AS t4_r14, `users`.`current_login_at` AS t4_r15, `users`.`last_login_at` AS t4_r16, `users`.`current_login_ip` AS t4_r17, `users`.`last_login_ip` AS t4_r18, `users`.`created_at` AS t4_r19, `users`.`updated_at` AS t4_r20, `keeper_users_facilities`.`id` AS t5_r0, `keeper_users_facilities`.`company_id` AS t5_r1, `keeper_users_facilities`.`building_id` AS t5_r2, `keeper_users_facilities`.`login` AS t5_r3, `keeper_users_facilities`.`name` AS t5_r4, `keeper_users_facilities`.`role` AS t5_r5, `keeper_users_facilities`.`email` AS t5_r6, `keeper_users_facilities`.`comments` AS t5_r7, `keeper_users_facilities`.`crypted_password` AS t5_r8, `keeper_users_facilities`.`password_salt` AS t5_r9, `keeper_users_facilities`.`persistence_token` AS t5_r10, `keeper_users_facilities`.`perishable_token` AS t5_r11, `keeper_users_facilities`.`login_count` AS t5_r12, `keeper_users_facilities`.`failed_login_count` AS t5_r13, `keeper_users_facilities`.`last_request_at` AS t5_r14, `keeper_users_facilities`.`current_login_at` AS t5_r15, `keeper_users_facilities`.`last_login_at` AS t5_r16, `keeper_users_facilities`.`current_login_ip` AS t5_r17, `keeper_users_facilities`.`last_login_ip` AS t5_r18, `keeper_users_facilities`.`created_at` AS t5_r19, `keeper_users_facilities`.`updated_at` AS t5_r20, `facility_types`.`id` AS t6_r0, `facility_types`.`name` AS t6_r1, `facility_types`.`desc` AS t6_r2, `facility_migrations`.`id` AS t7_r0, `facility_migrations`.`building_id` AS t7_r1, `facility_migrations`.`equipment_id` AS t7_r2, `facility_migrations`.`facility_id` AS t7_r3, `facility_migrations`.`created_at` AS t7_r4 
FROM `facilities` 
LEFT OUTER JOIN`buildings` ON `buildings`.`id` = `facilities`.`owner_building_id` 
LEFT OUTER JOIN`buildings` `delegated_buildings_facilities` ON `delegated_buildings_facilities`.`id` = `facilities`.`delegated_building_id` 
LEFT OUTER JOIN`buildings` `keeper_buildings_facilities` ON `keeper_buildings_facilities`.`id` = `facilities`.`keeper_building_id` 
LEFT OUTER JOIN`users` ON `users`.`id` = `facilities`.`owner_user_id` 
LEFT OUTER JOIN`users` `keeper_users_facilities` ON `keeper_users_facilities`.`id` = `facilities`.`keeper_user_id` 
LEFT OUTER JOIN`facility_types` ON `facility_types`.`id` = `facilities`.`type_id` 
LEFT OUTER JOIN`facility_migrations` ON `facility_migrations`.`facility_id` = `facilities`.`id` 
WHERE `facilities`.`id` IN (15, 47, 16, 48, 17, 49, 18, 50, 19, 51, 20, 52) AND ((1=1)) ORDER BY users.name ASC 

Vì vậy, làm thế nào tôi có thể sử dụng LEFT JOIN chỉ cho các lĩnh vực mà tôi có điều kiện (như đặt hàng) và SELECT đơn giản cho các bảng khác (như bao gồm thường xuyên làm việc khi không có điều kiện)?

+0

Tôi không quen thuộc với Rails nhưng '.all' ở cuối mã của bạn làm gì? –

+0

Tìm tất cả - Thao tác này sẽ trả về tất cả các bản ghi khớp với các tùy chọn được sử dụng. Từ đây: http://apidock.com/rails/ActiveRecord/FinderMethods/find – sunki

+0

Tôi nghĩ bạn nên sử dụng '.joins()' thay vì '.includes()' cho các bảng bạn muốn 'INNER JOIN' . Xem câu hỏi SO này: http://stackoverflow.com/questions/4861416/whats-the-difference-between-includes-and-joins-in-activerecord-query và hướng dẫn: http://guides.rubyonrails.org/ active_record_querying.html # join-tables –

Trả lời

0

Vì chưa có ai đề cập đến nó. Có một phương pháp được gọi là preload gần như giống như includes, ngoại trừ việc nó sử dụng các truy vấn riêng biệt thay vì tham gia trái.

Nếu bạn muốn kết nối bên ngoài bên trái chỉ để sắp xếp/nhóm/lọc và không được bao gồm trong kết quả, bạn có thể muốn sử dụng squeel gem thay vào đó, nó hỗ trợ kết nối bên ngoài theo phương pháp joins.

2

Tôi không hiểu đầy đủ câu hỏi; Nhưng bạn có nghĩa là bởi Ugly các 'Chọn A là B, X là Y'?

Nếu có, thì đây là cách Rails (AR) ánh xạ các giá trị trả về một cách chính xác cho (các) đối tượng tương ứng của chúng. Mọi t0_r * sẽ được ánh xạ tới một đối tượng cơ sở, bất kỳ t1_r * nào sẽ được ánh xạ tới đối tượng tòa nhà, v.v ...

Sử dụng select sẽ không giúp bạn ở đó.

Truy vấn này được Rails sử dụng nội bộ và theo như tôi nhớ, các ORM khác cũng làm như vậy (ví dụ như Hibernate).

+1

Chính xác, Hibernate (hoặc NHibernate) làm một cái gì đó rất giống nhau. – yfeldblum

+0

Không hoàn toàn như vậy. Thông thường AR không thực hiện JOIN nếu không có điều kiện trên các trường của bảng được liên kết (nó sử dụng các SELECT riêng biệt để tải các liên kết). Điều tôi muốn là sử dụng JOIN một lần trên bảng 'users' và SELECT cho các kết hợp khác. – sunki

+1

Bạn đúng về các câu lệnh riêng lẻ được sử dụng để tải các đối tượng liên quan. Nhưng, khi bạn sử dụng 'order (...)', bạn đang thực sự đặt hàng 'Facility' bởi người dùng. Nếu đó là 2 lựa chọn độc lập - bạn sẽ kết thúc với một 'người dùng' đã đặt hàng - sau đó là danh sách các cơ sở (không đặt hàng) có người dùng được liên kết từ truy vấn đầu tiên. – tamersalama

2

Rất tiếc, bạn không thể kết hợp các kết nối (: liên kết) (hoặc chọn, nhóm v.v.) bằng công cụ tải mong muốn "bao gồm (: liên kết)". Vì vậy, một giải pháp sẽ được cắt giảm truy vấn của bạn trong 2 mà luôn luôn là tốt hơn so với O (n) có hiệu lực của tải lười biếng hoặc tải rất nhiều đối tượng activerecord không mong muốn (rất tốn kém):

Thứ nhất nhận id cơ sở lọc mà không có hiệp hội bạn không cần phải tải

facilities_ids = Facility.select("facilities.id"). 
        joins([:conditional_associations, ...]).map(&:id) 

sau đó sử dụng chúng để lọc truy vấn háo hức của bạn với tất cả sự tốt lành của "bao gồm":

@facilities = Facility.includes([:loaded_associations]). 
       where(["facilities.id IN (?)", facilities_ids]) 
+0

Đó là cách mà tôi đang sử dụng ngay bây giờ. Nhưng tôi nghĩ có cách rõ ràng hơn. Dù sao cũng cảm ơn bạn! – sunki

+0

Một giải pháp khác ngụ ý rằng bạn không instansiating các hiệp hội, nhưng thay vì tải chúng như là các thuộc tính aliased (mà là khá tẻ nhạt). Ngoài ra với sự giúp đỡ của các đá quý tuyệt vời squeel bạn có thể kết hợp bên trong ans bên ngoài tham gia. bạn có quan tâm không? – charlysisto

0

Đây là một câu hỏi mô hình dữ liệu, không phải là một câu hỏi AR . AR chỉ vạch trần các vấn đề thiết kế lược đồ có thể xảy ra.

Cá nhân, tôi muốn điều tra lý do tại sao deligated_build nằm trong một bảng khác với tòa nhà. Chúng có thể nằm trong cùng một bảng, sau đó bạn có thể chỉ định ID tòa nhà được ủy quyền nào chỉ trong một mệnh đề truy vấn thay vì tham gia. Tương tự cho các bảng _building tương tự.

+0

Delegated_buildings nằm trong bảng tòa nhà. Tại sao bạn quyết định rằng nó không phải là? – sunki

+0

Họ tại sao bạn tham gia vào các tòa nhà như 4 lần? vì STI? –

Các vấn đề liên quan