![](/img/trans.png)
[英]SQL How To? Select all items from one table, and fill in the gaps based on another table?
[英]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現在將是具有兩個鍵true
和false
的哈希:
{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.