Saviez-vous que l'instruction SELECT est exécutée lorsque vous créez un modèle avec appartient_to défini dans Rails?
Par exemple, supposons que vous ayez le modèle suivant.
def User < ApplicationRecord
has_many :reviews
end
def Review < ApplicationRecord
belongs_to :user
belongs_to :book
end
def Book < ApplicationRecord
has_many :reviews
end
Si vous créez une révision à ce moment, SQL sera émis comme indiqué ci-dessous.
# id=1 utilisateur et un livre existent
irb(main):001:0> Review.create!(user_id: 1, book_id: 1)
(0.5ms) BEGIN
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Book Load (0.5ms) SELECT `books`.* FROM `books` WHERE `books`.`id` = 1 LIMIT 1
Review Create (0.6ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 1, '2020-06-30 14:31:27.343637', '2020-06-30 14:31:27.343637')
(2.0ms) COMMIT
Il est sélectionné pour les utilisateurs et les livres où appartient_to est défini.
La raison d'être SELECT est simple. appartient_to nécessite l'existence de modèles associés par défaut. Alors qu'en est-il des confirmations requises? Juste avant de le créer comme avant, il est sélectionné et vérifié.
Au fait, si l'existence du modèle associé est arbitraire, écrivez appartient_to: utilisateur, facultatif: vrai
.
Si vous l'écrivez ainsi, SELECT ne sera pas exécuté.
Voir aussi le guide des rails
https://railsguides.jp/association_basics.html#optional
Donc, le moyen d'éviter le SELECT mentionné dans le titre est de définir ʻoptional: true`! ... ne pas! !!
Bien sûr, SELECT ne sera plus émis, mais le réglage «optionnel: true» n'est pas approprié si le modèle associé est requis.
Alors, comment éviter SELECT sans définir ʻoptional: true`? Tout ce que vous avez à faire est de passer l'objet à créer.
irb(main):002:0> user = User.first
User Load (0.8ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, name: "1234567890", created_at: "2019-12-12 05:43:52", updated_at: "2019-12-12 05:43:52">
irb(main):003:0> book = Book.first
Book Load (0.6ms) SELECT `books`.* FROM `books` ORDER BY `books`.`id` ASC LIMIT 1
=> #<Book id: 1, title: "book1", created_at: "2020-06-15 14:21:15", updated_at: "2020-06-15 14:21:15">
irb(main):004:0> review = Review.create!(user: user, book: book)
(0.4ms) BEGIN
Review Create (0.6ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 1, '2020-06-30 14:32:14.911478', '2020-06-30 14:32:14.911478')
(2.0ms) COMMIT
En passant l'objet, l'existence peut être confirmée sans effectuer SELECT, donc l'instruction SELECT ne sera pas émise. Cependant, dans le cas de cet exemple, le nombre de SQL est le même car chaque modèle est sélectionné séparément juste avant !! Cependant, le nombre de SQL est complètement différent lors de la création de plusieurs avis du même utilisateur, par exemple.
Vous trouverez ci-dessous le code permettant de créer une revue des livres 1 à 5 pour user1 (partiellement omis pour améliorer la lisibilité). La première consiste à spécifier l'id et à créer.
irb(main):039:0> user = User.first
irb(main):040:0> books = Book.where(id: [1, 2, 3, 4, 5])
irb(main):042:0> books.each do |book|
irb(main):043:1* Review.create!(user_id: user.id, book_id: book.id)
irb(main):044:1> end
Book Load (0.8ms) SELECT `books`.* FROM `books` WHERE `books`.`id` IN (1, 2, 3, 4, 5)
(0.3ms) BEGIN
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Book Load (0.4ms) SELECT `books`.* FROM `books` WHERE `books`.`id` = 1 LIMIT 1
Review Create (0.8ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 1, '2020-06-30 15:10:00.009569', '2020-06-30 15:10:00.009569')
(3.6ms) COMMIT
(0.3ms) BEGIN
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Book Load (0.4ms) SELECT `books`.* FROM `books` WHERE `books`.`id` = 2 LIMIT 1
Review Create (0.4ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 2, '2020-06-30 15:10:00.020722', '2020-06-30 15:10:00.020722')
(1.8ms) COMMIT
(0.4ms) BEGIN
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Book Load (0.4ms) SELECT `books`.* FROM `books` WHERE `books`.`id` = 3 LIMIT 1
Review Create (0.4ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 3, '2020-06-30 15:10:00.029827', '2020-06-30 15:10:00.029827')
(1.9ms) COMMIT
(0.3ms) BEGIN
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Book Load (0.3ms) SELECT `books`.* FROM `books` WHERE `books`.`id` = 4 LIMIT 1
Review Create (0.3ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 4, '2020-06-30 15:10:00.037725', '2020-06-30 15:10:00.037725')
(1.7ms) COMMIT
(0.3ms) BEGIN
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Book Load (0.3ms) SELECT `books`.* FROM `books` WHERE `books`.`id` = 5 LIMIT 1
Review Create (0.4ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 5, '2020-06-30 15:10:00.045389', '2020-06-30 15:10:00.045389')
(1.6ms) COMMIT
Passez ensuite l'objet et créez-le.
irb(main):045:0> user = User.first
irb(main):046:0> books = Book.where(id: [1, 2, 3, 4, 5])
irb(main):047:0> books.each do |book|
irb(main):048:1* Review.create!(user: user, book: book)
irb(main):049:1> end
Book Load (0.8ms) SELECT `books`.* FROM `books` WHERE `books`.`id` IN (1, 2, 3, 4, 5)
(0.5ms) BEGIN
Review Create (0.5ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 1, '2020-06-30 15:12:05.610003', '2020-06-30 15:12:05.610003')
(2.8ms) COMMIT
(0.4ms) BEGIN
Review Create (0.4ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 2, '2020-06-30 15:12:05.617125', '2020-06-30 15:12:05.617125')
(1.7ms) COMMIT
(0.3ms) BEGIN
Review Create (0.5ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 3, '2020-06-30 15:12:05.622432', '2020-06-30 15:12:05.622432')
(1.8ms) COMMIT
(0.4ms) BEGIN
Review Create (0.5ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 4, '2020-06-30 15:12:05.627957', '2020-06-30 15:12:05.627957')
(2.0ms) COMMIT
(0.4ms) BEGIN
Review Create (0.6ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 5, '2020-06-30 15:12:05.634191', '2020-06-30 15:12:05.634191')
(1.8ms) COMMIT
Avec la deuxième implémentation, 10 SELECT peuvent être omis !! Gardez à l'esprit que le SQL émis dépend de votre connaissance ou non!
Comme mentionné ci-dessus, l'existence d'appartient_to est vérifiée lors de la création, mais il semble que le modèle passé par create et le résultat de SELECT juste avant ne soient pas seulement utilisés pour le contrôle d'existence mais aussi correctement définis dans l'association.
SQL n'est pas émis même si vous faites référence à l'association du modèle créé par create.
irb(main):051:0> review = Review.create!(user: user, book: book)
(2.3ms) BEGIN
Review Create (0.6ms) INSERT INTO `reviews` (`user_id`, `book_id`, `created_at`, `updated_at`) VALUES (1, 1, '2020-06-30 15:20:01.755647', '2020-06-30 15:20:01.755647')
(2.5ms) COMMIT
=> #<Review id: 36, content: "", user_id: 1, book_id: 1, status: "draft", created_at: "2020-06-30 15:20:01", updated_at: "2020-06-30 15:20:01">
#Puisque le modèle passé au moment de la création est défini, SELECT n'est pas exécuté même si l'association est référencée.
irb(main):052:0> review.user
=> #<User id: 1, name: "1234567890", created_at: "2019-12-12 05:43:52", updated_at: "2019-12-12 05:43:52">
irb(main):053:0> review.book
=> #<Book id: 1, title: "book1", created_at: "2020-06-15 14:21:15", updated_at: "2020-06-15 14:21:15">
Recommended Posts