簡體   English   中英

如何基於另一個表中的HABTM關系從一個表中選擇項目? Postgres“ ALL”不起作用,

[英]How to select items from one table based on HABTM relation in another? Postgres “ALL” does not work,

我正在嘗試檢索具有某些類別的漫畫(漫畫)。 例如,在下面的代碼中,我嘗試搜索Adventure(id = 29)和Comedy(id = 25)mangas。 我正在使用“ ALL”運算符,因為我希望兩個類別都在mangas中。 (即,通過關系表返回所有類別均為25和29的漫畫,但也可以附加其他類別)

@search = Manga.find_by_sql("
    SELECT m.*
    FROM mangas m
    JOIN categorizations c ON c.manga_id = m.id AND c.category_id = ALL (array[29,25])
")

問題? 該查詢無法正常工作(也許我誤解了ALL運算符)。 我沒有從查詢中得到任何回報。 所以我嘗試將其更改為

JOIN categorizations c ON c.manga_id = m.id AND c.category_id >= ALL (array[29,25])

我得到ID大於29的mangas。我什至沒有得到類別29。 我在這里缺少什么嗎?

查詢也很慢。 如果有人帶有查詢返回我想要的東西,我將不勝感激。

我正在使用Ruby on Rails 4.2和postgresql謝謝

更新:(過帳模型關系)

class Manga < ActiveRecord::Base
  has_many :categorizations, :dependent => :destroy
  has_many :categories, through: :categorizations
end
class Category < ActiveRecord::Base
  has_many :categorizations, :dependent => :destroy
  has_many :mangas, through: :categorizations
end
class Categorization < ActiveRecord::Base
  belongs_to :manga
  belongs_to :category
end

我的嘗試基於@Beartech答案:

    wheres = categories_array.join(" = ANY (cat_ids) AND ")+" = ANY (cat_ids)"
    @m = Manga.find_by_sql("
    SELECT mangas.*
    FROM
      (SELECT manga_id, cat_ids
       FROM
       (
         SELECT c.manga_id, array_agg(c.category_id) cat_ids
         FROM categorizations c GROUP BY c.manga_id
        )
        AS sub_table1 WHERE #{wheres}
      )
      AS sub_table2
      INNER JOIN mangas ON sub_table2.manga_id = mangas.id
    ")

我將其添加為其他答案,因為出於歷史原因,我希望選擇另一個。 它可以完成工作,但效率不高,因此也許有人會看到可以改進的地方。 那個...

答案是!!!

這一切又回到身邊PostgreSQL的功能ALL是不是你想要的。 您需要“ CONTAINS”運算符,即@> 您還需要某種聚合函數,因為您想將每個漫畫與其所有類別進行匹配,請僅選擇包含25和29的漫畫。

這是為此的SQL:

SELECT manga.*
FROM
  (SELECT manga_id, cat_ids
   FROM
     (SELECT manga_id, array_agg(category_id) cat_ids
      FROM categorizations GROUP BY manga_id)
       AS sub_table1 WHERE cat_ids @> ARRAY[25,29] )
    AS sub_table2
  INNER JOIN manga
    ON sub_table2.manga_id = manga.id
;

因此,您要提取一個子查詢,該子查詢將獲取聯接表中所有匹配的行,將其類別ID放入數組中,並按漫畫ID進行分組。 現在,您可以將其加入到漫畫表中以獲取實際的漫畫記錄

紅寶石看起來像:

@search = Manga.find_by_sql("SELECT manga.* FROM (SELECT manga_id, cat_ids FROM (SELECT manga_id, array_agg(category_id) cat_ids FROM categorizations GROUP BY manga_id) AS sub_table1 WHERE cat_ids @> ARRAY[25,29] ) AS sub_table2 INNER JOIN manga ON sub_table2.manga_id = manga.id

它快速干凈,可以在本機SQL中完成所有操作。

您可以將變量插值到.find_by_sql()文本中。 由於@>詢問類別數組是否包含所有搜索詞,因此這為您提供了即時搜索功能。

terms = [25,29]
q = %Q(SELECT manga.* FROM (SELECT manga_id, cat_ids FROM (SELECT manga_id, array_agg(category_id) cat_ids FROM categorizations GROUP BY manga_id) AS sub_table1 WHERE cat_ids @> ARRAY#{terms} ) AS sub_table2 INNER JOIN manga ON sub_table2.manga_id = manga.id")
Manga.find_by_sql(q)

重要

我相當確定上述代碼在某種程度上是不安全的。 我假設您將以某種方式驗證數組的輸入,即

terms.all? {|term| term.is_a? Integer} ? terms : terms = []

第三次魅力,對不對? 大聲笑

好的,完全改變了我的答案,因為在Rails中看起來應該很簡單,但是卻讓我煩惱不已。

我在很大程度上取決於這個答案來提出這個。 您應該在您的Manga模型中放置一個范圍:

class Manga < ActiveRecord::Base
  has_many :categorizations, :dependent => :destroy
  has_many :categories, through: :categorizations

  scope :in_categories, lambda { |*search_categories|
    joins(:categories).where(:categorizations => { :category_id => search_categories } )
    }

end

然后像這樣調用它:

@search = Manga.in_categories(25,29).group_by {|manga| ([25,29] & manga.category_ids) == [25,29]}

此遍歷所有包含至少一個或多個的兩個類別的漫畫的,使得一個陣列的“置位” [25,29]與來自陣列manga.category_ids並檢查,看看是否能集等於您的要求的集合。 這清除了只有兩個鍵之一的所有漫畫。

@search現在將是具有兩個鍵truefalse的哈希:

{true =>  [#<Manga id: 9, name: 'Guardians of...

     .... multiple manga objects that belong to at least 
          the two categories requested but not eliminated if
           they also belong to a third of fourth category ... ]

 false => [ ... any Manga that only belong to ONE of the two
                categories requested ...  ]
     }

現在,要獲取屬於兩個類別的唯一Mangas,請使用.uniq:

@search[true].uniq

繁榮!! 您有一系列與兩個類別都匹配的Manga對象。

要么

您可以使用以下方法簡化它:

@search = Manga.in_categories(25,29).keep_if {|manga| ([25,29] & manga.category_ids) == [25,29]}
@search.uniq!

我喜歡它好一點,看起來更干凈。

現在為您提供SQL JUNKIES

@search = Manga.find_by_sql("Select * 
  FROM categorizations
   JOIN manga ON categorizations.manga_id = manga.id 
   WHERE categorizations.cateogry_id IN (25,29)").keep_if {|manga| ([25,29] & manga.category_ids) == [25,29]}

@search.uniq!

* OK OK OK我將在此之后停止。 :-) *

將其全部滾動到Manga.rb的作用域中:

scope :in_categories, lambda { |*search_categories|
joins(:categories).where(:categorizations => { :category_id => search_categories } ).uniq!.keep_if {|manga| manga.category_ids.include? search_categories[0] and manga.category_ids.include? search_categories[1]} }

有一種更便捷的方式嗎? (實際上,最后一個很簡單)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM