【问题标题】:SQL noob needs help writing a Rails query involving joins and maybe intersectSQL noob 需要帮助编写一个涉及连接和可能相交的 Rails 查询
【发布时间】:2009-06-17 20:35:07
【问题描述】:

我有 3 个模型:Books、Notifications 和 NotificationTypes。书籍具有通知,因为 Notification 模型具有 book_id。 Notifications 有一个 notification_type,因为 Notification 模型有一个 notification_type_id

我想要在 date1 和 date2 之间创建的所有图书

books_set1 = Book.find :all, :conditions => ["created_at <= ? AND show_time >= ?", max_date, min_date]

但我不想要具有 notification.notification_type_id = 1 通知的书籍,理想情况下我想通过引用 notification_type.name 来说明这一点,所以我不想要具有 notification.notification_type.name = 'type1 通知的书籍'

如果已经为一本书创建了 type1 的通知,我不希望它在集合中返回(因为我将使用返回的集合创建该类型的通知)。

我不确定是否有办法在一个查询中执行此操作,我想我需要 2 个查询和 INTERSECT - 第一个我已经包含在这篇文章中,第二个我不确定。但在伪代码中,我认为这是它需要做的:

notification_type_id = Notification.find_by_name('type1')
get all notifications where notification_id = notification_type_id
set2 = get the associated book set from the notification set (since each notification has one book)

然后我做 set1 - set2

更新

感谢一些帮助,我编写了两个查询,得到了我想要的结果。如果有人知道怎么做,我很乐意在 1 个查询中使用它:

books_in_range = Book.find :all, :conditions => ["created_at <= ? AND created_at >= ?", max_date, min_date]
books_without_these_notifications = Book.find(:all, :joins => { :notifications => :notification_type }, :conditions => ["notification_types.name = ?","type1"] )
books_to_consider = books_in_range - books_without_these_reminders

同样,我们的想法是让所有没有创建 type1 通知并在特定日期范围内的书籍。

【问题讨论】:

  • 您是否尝试过我的答案中的 NOT EXISTS 选项?这几乎肯定会比获取日期范围内的所有书籍然后获取所有带有 type1 通知的书籍更有效。
  • 它不能开箱即用,notification_id 未定义...但我正在修复它,并将让您随时更新。谢谢你提醒我....我挂断了 molf 的回答
  • 我已经编辑修复 - 书籍和通知之间的关联方式错误。
  • “notification_type_id”应该是“notifications.notification_type_id”,“book_id”应该是“notifications.book_id”。除此之外....见鬼!
  • 很高兴它成功了(终于!) - 我已经更新了我的答案以包含通知表名称。

标签: sql ruby-on-rails


【解决方案1】:

以下应该有效:

Book.scoped(:conditions => ["created_at <= ? AND show_time >= ?", max_date,
  min_date]).find(:all, :joins => { :notifications => :notification_types },
  :conditions => ["notification_type.name <> ?", "type1"])

为了更加清晰,将一些参数提取到named_scopes 或局部变量中。

更新以反映否定。

【讨论】:

  • 嗯...试图剖析这个。一个问题是一本书没有很多提醒(理论上确实如此,但我让用户有很多提醒,因为这是我通常访问它们的方式)。这也不会返回所有具有通知类型1的书籍吗?我想得到没有通知类型1的书
  • 我认为您的意思是在 :conditions 之前放置一个左括号?看起来有语法错误....仍在测试查询,但这开始很有意义
  • 这不会从关联通知中排除 type1 的通知,但仍会返回所有书籍吗?
  • @Shadwell,啊,是的。当 :include 不再生成 JOIN 而是生成第二个查询时,这在 Rails 2 中发生了变化。我们应该使用 :joins 代替。
  • @molf 我当时以为我快疯了 - 错过了加入的编辑。我认为您需要添加 :select => 'distinct books.*' 以确保您不会返回重复项。但是,它会返回根本没有通知的书籍吗?
【解决方案2】:

你可以用 NOT EXISTS 做到这一点,但我不确定它的效率如何:

Book.find(
  :all, 
  :conditions => 
    ['created_at <= ? AND show_time >= ? AND 
      NOT EXISTS 
        (SELECT * FROM notifications 
         INNER JOIN notification_types ON notifications.notification_type_id = notification_types.id 
         WHERE notification_types.name = ? AND notifications.book_id = books.id)',
     max_date, min_date, 'type1'
    ])

您也许可以从不同的方向进入并跟踪那些通过向书籍添加布尔值或直接连接到书籍的其他对象发出类型 1 通知的人.那么您的查询可能是:

Book.find(:all, 
  :conditions => 
    ['created_at <= ? AND show_time >= ? AND 
      type1_notification_sent = ?', 
     max_date, min_date, false
    ])

【讨论】:

  • 我会,但是到这个项目结束时可能会有大约 30 种类型的通知
【解决方案3】:
SELECT books.* FROM books WHERE books.created_at <= ? AND books.show_time >= ? AND 
  notifications.name <> 'type1'
LEFT OUTER JOIN notifications ON notifications.book_id = books.id AND 
  notification.name = 'type1'

我想这会奏效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    • 1970-01-01
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多