简体   繁体   English

如何基于另一个表中的HABTM关系从一个表中选择项目? Postgres“ ALL”不起作用,

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

I am trying to retrieve mangas (comics) that have certain categories. 我正在尝试检索具有某些类别的漫画(漫画)。 For example in the code below, I am trying to search for Adventure(id=29) and Comedy(id=25) mangas. 例如,在下面的代码中,我尝试搜索Adventure(id = 29)和Comedy(id = 25)mangas。 I am using "ALL" operator because I want BOTH categories be in mangas. 我正在使用“ ALL”运算符,因为我希望两个类别都在mangas中。 (ie return all Manga that have both a category of 25 AND 29 through the relation table, but can also have other categories attached to them) (即,通过关系表返回所有类别均为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])
")

Problems? 问题? The query is not working as I am expecting (maybe I misunderstand something about ALL operator). 该查询无法正常工作(也许我误解了ALL运算符)。 I am getting nothing back from the query. 我没有从查询中得到任何回报。 So I tried to change it to 所以我尝试将其更改为

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

I get back mangas whose IDs are GREATER than 29. I am not even getting category #29. 我得到ID大于29的mangas。我什至没有得到类别29。 Is there something I am missing here? 我在这里缺少什么吗?

Also the query is... VERY slow. 查询也很慢。 I would appreciate it if someone comes with a query that return back what I want. 如果有人带有查询返回我想要的东西,我将不胜感激。

I am using Ruby on Rails 4.2 and postgresql Thanks 我正在使用Ruby on Rails 4.2和postgresql谢谢

Update: (posting models relationship) 更新:(过帐模型关系)

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

My attempt based on @Beartech answer: 我的尝试基于@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
    ")

I'm adding this as a different answer, because I like to have the other one for historic reasons. 我将其添加为其他答案,因为出于历史原因,我希望选择另一个。 It gets the job done, but not efficiently, so maybe someone will see where it can be improved. 它可以完成工作,但效率不高,因此也许有人会看到可以改进的地方。 That said... 那个...

THE ANSWER IS!!! 答案是!!!

It all comes back around to the Postgresql functions ALL is not what you want. 这一切又回到身边PostgreSQL的功能ALL是不是你想要的。 You want the "CONTAINS" operator, which is @> . 您需要“ CONTAINS”运算符,即@> You also need some sort of aggregate function because you want to match each Manga with all of it's categories, select only the ones that contain both 25 and 29. 您还需要某种聚合函数,因为您想将每个漫画与其所有类别进行匹配,请仅选择包含25和29的漫画。

Here is the sql for that: 这是为此的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
;

So you are pulling a subquery that grabs all of the matching rows in the join table, puts their category ids into an array, and grouping by the manga id. 因此,您要提取一个子查询,该子查询将获取联接表中所有匹配的行,将其类别ID放入数组中,并按漫画ID进行分组。 Now you can join that against the manga table to get the actual manga records 现在,您可以将其加入到漫画表中以获取实际的漫画记录

The ruby looks like: 红宝石看起来像:

@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

It's fast and clean, doing it all in the native SQL. 它快速干净,可以在本机SQL中完成所有操作。

You can interpolate variables into the .find_by_sql() text. 您可以将变量插值到.find_by_sql()文本中。 This gives you an instant search function since @> is asking if the array of categories contains all of the search terms. 由于@>询问类别数组是否包含所有搜索词,因此这为您提供了即时搜索功能。

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)

Important 重要

I am fairly certain that the above code is in some way insecure. 我相当确定上述代码在某种程度上是不安全的。 I would assume that you are going to validate the input of the array in some way, ie 我假设您将以某种方式验证数组的输入,即

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

Third times the charm, right? 第三次魅力,对不对? LOL 大声笑

OK, totally changing my answer because it seems like this should be SUPER EASY in Rails, but it has stumped the heck out of me... 好的,完全改变了我的答案,因为在Rails中看起来应该很简单,但是却让我烦恼不已。

I am heavily depending on This answer to come up with this. 我在很大程度上取决于这个答案来提出这个。 You should put a scope in your Manga model: 您应该在您的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

Then call it like: 然后像这样调用它:

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

This iterates through all of the Manga that contain at least ONE or more of the two categories, makes a "set" of the array of [25,29] with the array from the manga.category_ids and checks to see if that set equals your reqeusted set. 此遍历所有包含至少一个或多个的两个类别的漫画的,使得一个阵列的“置位” [25,29]与来自阵列manga.category_ids并检查,看看是否能集等于您的要求的集合。 This weeds out ALL Manga that only have one of the two keys. 这清除了只有两个键之一的所有漫画。

@search will now be a hash with two keys true and false : @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 ...  ]
     }

Now to get just the unique Mangas that belong to BOTH categories use .uniq: 现在,要获取属于两个类别的唯一Mangas,请使用.uniq:

@search[true].uniq

BOOM!! 繁荣!! You have an array of you Manga objects that match BOTH of your categories. 您有一系列与两个类别都匹配的Manga对象。

OR 要么

You can simplify it with: 您可以使用以下方法简化它:

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

I like that a little bit better, it looks cleaner. 我喜欢它好一点,看起来更干净。

AND NOW FOR YOU SQL JUNKIES 现在为您提供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 I'll stop after this one. * OK OK OK我将在此之后停止。 :-) * :-) *

Roll it all into the scope in Manga.rb: 将其全部滚动到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]} }

THERE HAS GOT TO BE AN EASIER WAY??? 有一种更便捷的方式吗? (actually that last one is pretty easy) (实际上,最后一个很简单)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 SQL怎么做? 从一个表中选择所有项目,然后根据另一张表填充空白? - SQL How To? Select all items from one table, and fill in the gaps based on another table? 如果另一个值不存在,如何根据一个值从表中选择项目? (口才/ SQL) - How to select items from a table based on one value if another value does not exists? (eloquent/sql) 如何从一张表中选择关系表中不存在的所有记录? - how to select all records from one table that not exist in relation table? Select 基于条件的一个表中的所有记录,而不是另一个表中的所有记录 - Select all records from one table not in another table based on a condition 如何从一个表中选择所有行,并根据另一表计算字段值 - How can I select all rows from one table, calculating a field value based on another table Postgres,从一个表中获取所有项目,其中 id 不在另一个表 JSON 数组列中? - Postgres, get all items from one table which ids are not in another tables JSON array column? 列出一个表中不在另一个表中的所有项目 - list all items from one table that are not in another 如何根据一个值从表一中获取 select,然后从同一表中获取 select 以及该表中的值? - How to select all from table one based on a value, then select from same table with value from that table? 根据从另一表中选择的结果从一个表中选择 - Select from one table based on results of select from another table 如何从一个表中选择项目,以使另一表中的查询恰好产生一行? - How do I select items from one table such that a query in another table yields exactly one row?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM