SQL noob需要帮助编写涉及连接和可能相交的Rails查询

时间:2009-06-17 20:35:08

标签: sql ruby-on-rails

我有3个模型:Books,Notifications和NotificationTypes。由于通知模型具有book_id,因此书籍会发出通知。通知具有一个notification_type,因为通知模型具有一个notification_type_id

我希望在date1和date2之间创建所有图书

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

但是我不希望有通知通知的书籍.notification_type_id = 1,理想情况下我想通过引用notification_type.name来说这个,所以我不想要有通知通知的书籍.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通知的书籍都在特定的日期范围内。

3 个答案:

答案 0 :(得分:3)

以下内容应该有效:

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

将一些参数提取到named_scope或局部变量中,以便更清晰。

更新以反映否定。

答案 1 :(得分: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'
    ])

您可以从不同的方向来看看,并通过向书籍或其他直接加入书籍的对象添加布尔值来跟踪那些 已发出type1通知的内容。然后您的查询可能是:

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

答案 2 :(得分:0)

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'

这会奏效,我想。