【问题标题】:How to join conditionally and get data from the join table?如何有条件地连接并从连接表中获取数据?
【发布时间】:2019-06-02 04:49:02
【问题描述】:

我有两张桌子,ProductProductNewsletter。一个产品可以有多个product_newsletters,因此在rails 中,产品一侧是has_many 关系,product_newsletter 一侧是belongs_to,带有product_id。表格看起来像这样:

products
id title... etc

product_newsletters
id product_id date

我想获取在某个日期范围内具有 product_newsletter 的产品,并从查询中找到的 product_newsletter 中选择日期。因此,如果一个产品有两个 product_newsletter,日期分别为 11-03-2018、20-03-2018 和 04-04-2018,则查询日期在 01-03-2018 和 30-01-03-2018 之间的产品应该显示两次,一次是 11-03-2018,另一个是 20-03-2018。起初我用子查询尝试了这个连接:

SELECT *
     , news.date AS newsletter_date 
  FROM products 
  JOIN 
     ( SELECT newsletter_products.* 
         FROM newsletter_products 
        WHERE newsletters.date BETWEEN '2018-01-03' AND '2018-01-20'
     ) news 
    ON  products.id = news.product_id

但是这个连接导致产品的 id 与子查询中的那些时事通讯相同,而不是我对子查询的预期 product_id 中的那些。我在 Rails 和 Mysql 上使用 ruby​​。我的想法是使用这个查询的结果来简单地做一个 ruby​​ products.group_by(&:newsletter_date) 并最终得到一个很好的哈希,其中日期作为键,产品作为值,没有额外的查询或过滤每个日期,这是我当前的实现。

【问题讨论】:

  • 该查询看起来没问题。不过,我不会使用子查询,而只是直接在表上进行简单的连接,并使用 where 子句指定日期范围。我不太了解您的问题描述,但我认为这与您选择 * 的事实有关,这意味着两个表(产品表和子查询/派生表)中的所有列。我只会选择确切的列你想要。如果你想要两个表中的一个列具有相同的名称,你可以为该列提供一个别名(例如products.id as product_id, news.id as news_id)。
  • 永远不要使用SELECT *

标签: mysql sql ruby-on-rails ruby


【解决方案1】:

以下将为您提供预期的输出,

Product.joins(:product_newsletters).where(product_newsletters: { date: ['19-01-2013'.to_datetime..'19-02-2013'.to_datetime] })

如果 2 个 product_newsletters 对象在日期范围内并且都具有相同的 product_id,那么对于上述查询,您仍将获得相同产品 ID 的 2 counts

【讨论】:

  • 谢谢!我最终使用它作为基础,最终拆分了 product_newsletters 查询并在末尾添加了额外的 select 子句,以便稍后我可以做一个 ruby​​ group_by。所以最后它是这样的:Product.joins(:newsletter_products).merge(newsletter_products). select('products.*, newsletter_products.date AS newsletter_product_date') 这实际上呈现了类似于 Gordon 的通用 sql 答案的东西,将其标记为已接受但都是有效答案。
【解决方案2】:

你应该这样写查询:

SELECT p.*, n.date AS newsletter_date
FROM products p INNER JOIN 
     newsletter_products np
     ON p.id = n.product_id
WHERE np.date BETWEEN '2018-01-03' AND '2018-01-20';

注意事项:

  • 不要使用select *。您将获得两次通讯日期。
  • 子查询是不必要的,可能会妨碍优化。
  • 使用表别名,以便查询更易于编写和阅读。
  • 我不喜欢between 有日期,但这是你正在做的逻辑。当date 没有时间分量时是安全的。

【讨论】:

    猜你喜欢
    • 2021-08-10
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    • 2017-12-06
    相关资源
    最近更新 更多