Find records where join doesn't exist

后端 未结 3 1693
死守一世寂寞
死守一世寂寞 2020-11-30 07:19

I have a scope to limit all questions by whether or not a user has voted on them. In the model:

scope :answered_by, lambda {|u| joins(:votes).wh         


        
相关标签:
3条回答
  • 2020-11-30 07:34

    And if you want to do EXISTS query in elegant and Rails-ish manner, you can use Where Exists gem I've written:

    Question.where_not_exists(:votes, user_id: current_user.id)
    

    Of course, you can made scope of it as well:

    scope :unanswered_by, ->(user){ where_not_exists(:votes, user_id: user.id) }
    
    0 讨论(0)
  • 2020-11-30 07:46

    Use an EXISTS expression:

    WHERE NOT EXISTS (
       SELECT FROM votes v  -- SELECT list can be empty
       WHERE  v.some_id = base_table.some_id
       AND    v.user_id = ?
       )
    

    The difference

    ... between NOT EXISTS() (Ⓔ) and NOT IN() (Ⓘ) is twofold:

    1. Performance

      Ⓔ is generally faster. It stops processing the subquery as soon as the first match is found. The manual:

      The subquery will generally only be executed long enough to determine whether at least one row is returned, not all the way to completion.

      Ⓘ can also be optimized by the query planner, but to a lesser extent since NULL handling makes it more complex.

    2. Correctness

      If one of the resulting values in the subquery expression is NULL, the result of Ⓘ is NULL, while common logic would expect TRUE - and Ⓔ will return TRUE. The manual:

      If all the per-row results are either unequal or null, with at least one null, then the result of NOT IN is null.

    Essentially, (NOT) EXISTS is the better choice in most cases.

    Example

    Your query can look like this:

    SELECT *
    FROM   questions q
    WHERE  NOT EXISTS (
        SELECT FROM votes v 
        WHERE  v.question_id = q.id
        AND    v.user_id = ?
        );
    

    Do not join to votes in the base query. That would void the effort.

    Besides NOT EXISTS and NOT IN there are additional syntax options with LEFT JOIN / IS NULL and EXCEPT. See:

    • Select rows which are not present in other table
    0 讨论(0)
  • 2020-11-30 07:47

    try this and let me know if it works

    EDIT-1

    scope :unanswered_questions, lambda { joins('LEFT OUTER JOIN votes ON questions.id = votes.question_id').where('votes.question_id IS NULL') }
    

    EDIT-2

    scope :unanswered_by, lambda {|u| where("questions.id NOT IN (SELECT votes.question_id from votes where votes.user_id = ?)",u.id) }
    
    0 讨论(0)
提交回复
热议问题