平々凡々エンジニア

平凡で難しい悩みを解決

rails find_byメソッドにシンボル名入れなかった結果 user.all.firstと同等の検索結果になった

やりたかった事

User.find_by(id: params[:id])
#<User id: 2, name: "藤田 悠人", email: "example-1@railstutorial.org", created_at: "2019-12-13 12:27:47", updated_at: "2019-12-13 12:27:47", password_digest: "$2a$12$isF697OdUbU0ZXulWoQLF.FqGHDDDVsLP1OWSA2.Cxy...", remember_digest: nil, admin: false>
(byebug)   CACHE User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  ↳ (byebug):1

params[:id]で指定した通りに検索をする

実際にしてしまった事

User.find_by( params[:id])
#<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-12-13 12:27:46", updated_at: "2019-12-13 12:27:46", password_digest: "$2a$12$PJlyzzsayMdJBAMOVResV.qr/8aRUWsywR8WzutYrhX...", remember_digest: nil, admin: true>
(byebug)   CACHE User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE (2) LIMIT ?  [["LIMIT", 1]]
  ↳ (byebug):1

params[:id]にどのような数値が入ってもユーザーカラムの一番最初のコードが読まれてしまう

エラー原因は簡単で謝ってシンボル名を書き忘れたことです

正しくないUser.find_by( params[:id])
正しいUser.find_by(id: params[:id])

原因を探すまで時間がかかってしまいましたが見つけたあとはすぐにミスがわかりました。

ですがUser.find_by( params[:id])の実行結果がなぜ一番最初のコードが読み込まれてしまうのかわかりませんでした。

SELECT "users".* FROM "users" WHERE (2) LIMIT ? [["LIMIT", 1]]をわかる範囲で意味を説明してみたものを以下に示します。

SELECT "users".*→ユーザーのテーブルカラム全て
FROM "users"→ユーザーテーブル
WHERE (2)→params[:id]に格納していた:idの値2が入った
LIMIT ? [["LIMIT", 1]]→取得するデータ数を1に制限する

WHEREが(2)になるとどのような挙動になるかわからなかったので調べました。

参考:MySQL

MySQLでは、ゼロ以外の任意の非 NULL 値が TRUE に評価されます。

ということはWHERE句のはゼロ以外なのでWHEREの結果はTRUEになります。

このことからユーザーテーブルカラム全てからテーブルの一番最初のコードを検索するというsqlになるのでuser.all.firstと同じ結果と同等ということがわかりました。

シンボルを入れないとruby側がエラーを出力してくれそうなものですが出力しないこともあるのことを勉強できました。