0

これは Rails の基本的なことのはずですが、やり方がわかりません。

参加者が話す言語に基づいて参加者をフィルタリングしたいと思います。人々は複数の言語を話すことができ、言語は 1 対多の関係で独自のテーブルに格納されます。

今、私の検索は本当にぎこちなく見え、うまくいかないようです:

  if @cvsearch.language.present? == true and @cvsearch.language != 0
    @p = @p.joins(:languages).where('languages.name = ?', @cvsearch.language)  
  else
    @cvsearch.language = 0
  end

  if @cvsearch.language1.present? == true and @cvsearch.language1 != 0
    @p = @p.joins(:languages).where('languages.name = ?', @cvsearch.language1)  
  end

  if @cvsearch.language2.present? == true and @cvsearch.language2 != 0
    @p = @p.joins(:languages).where('languages.name = ?', @cvsearch.language2)  
  end

  if @cvsearch.language3.present? == true and @cvsearch.language3 != 0
    @p = @p.joins(:languages).where('languages.name = ?', @cvsearch.language3)  
  end

結果の SQL は、わずかに短縮されています。

SELECT COUNT(*) FROM "participants" INNER JOIN "languages" ON "languages"."participant_id" = "participants"."id" WHERE (participants.id >= 2) AND (languages.name = 11) AND (languages.name = 10)[0m

特定の解決策を得ることは素晴らしいことですが、これについてどこで読むことができるかについてのポインタはさらに良いでしょう - この問題を説明するために欠けているキーワードは何ですか?

だから、これは私が今使っている解決策です:

  if @cvsearch.language1.present? == true and @cvsearch.language1 != 0
    safe_lang = ActiveRecord::Base::sanitize(@cvsearch.language1)
    qry = "INNER JOIN languages l1 ON l1.participant_id = participants.id AND l1.name = " + safe_lang.to_s
    @p = @p.joins(qry)
  end

このアプローチの安全性に関するフィードバックを得る必要があります。

4

1 に答える 1

1

参照する一般的なリファレンスはわかりませんが、これは基本的な SQL に関するものです。基本的に、JOIN最初に が実行されて多数の行が生成され、次にWHEREが適用されて行がフィルタリングされます。ここでの概念上の間違いは、WHERE句が一致する言語の完全なセットに何らかの形で適用されると考えていることですが、そのようには機能しません。結果の各行は分離されていると見なされるため、言語のような句は何も返され(languages.name = 11) AND (languages.name = 10)ませ。 .name は、各行に 1 つの値しかありません。構築されたクエリは句に対してのみ機能するOR可能性があるため、次のように言うことができますWHERE (languages.name = 11) OR (languages.name = 12)

参加者を絞り込むには、言語ごとに 1 つの参加が必要なので、次のようにします。

SELECT COUNT(*) FROM participants
INNER JOIN languages l1 ON l1.participant_id = participants.id AND (languages.name = 10)
INNER JOIN languages l2 ON l2.participant_id = participants.id AND (languages.name = 11)
WHERE participants.id >= 2

ActiveRecord でこれを行う最も簡単な方法はよくわかりません。これは非常に一般的なクエリではありません。一般的な構造は機能するはずですが、次のようなものがあります。

if @cvsearch.language1.present? == true and @cvsearch.language1 != 0
  safe_language = ActiveRecord::Base.sanitize(@cvssearch.language1)
  join_clause   = "INNER JOIN languages l1 ON l1.participant_id = participants.id AND language.name = #{safe_language}"
  @p = @p.joins(join_clause)
end
于 2013-06-04T21:27:27.447 に答える