给定记录和订单条件,在之后或之前查找记录

时间:2014-03-16 17:58:29

标签: ruby-on-rails ruby activerecord

例如,您有一个按优先级排序的项目列表。你有10,000件物品!如果您向用户显示单个项目,如何为用户提供按钮以查看上一个项目或下一个项目(这些项目是什么)?

您可以将项目的位置传递给项目页面,并在SQL查询中使用OFFSET。除了必须传递可能改变的数字之外,其缺点是数据库无法跳转到偏移量;它必须读取每条记录,直到它达到第9001条记录。这很慢。寻找解决方案后,我找不到一个,所以我写了order_query

order_query使用相同的ORDER BY查询,但还包括一个WHERE子句,它排除当前(下一个)之前或之后(对于prev)的记录。

以下是标准的示例(使用上面的gem):

p = Issue.find(31).relative_order_by_query(Issue.visible, 
      [[:priority, %w(high medium low)],
       [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
       [:updated_at, :desc],
       [:id, :desc]])

p.before     #=> ActiveRecord::Relation<...>
p.previous   #=> Issue<...>
p.position   #=> 5
p.next       #=> Issue<...>
p.after      #=> ActiveRecord::Relation<...>

我刚刚在这里重新发明了轮子吗?我对在后端执行此操作的其他方法非常感兴趣。

这个gem内部构建一个依赖于当前记录的订单值的查询,如下所示:

SELECT ... WHERE    
x0 OR
y0 AND (x1 OR
        y1 AND (x2 OR
                y2 AND ...))
ORDER BY ...
LIMIT 1

x对应> / <条款,y=条款(解决关系),每个订单标准。

来自测试套件日志的示例查询:

-- Current record: priority='high' (votes - suspicious_votes)=4 updated_at='2014-03-19 10:23:18.671039' id=9
SELECT  "issues".* FROM "issues"  WHERE 
  ("issues"."priority" IN ('medium','low') OR 
   "issues"."priority" = 'high' AND (
       (votes - suspicious_votes) < 4 OR 
       (votes - suspicious_votes) = 4 AND (
           "issues"."updated_at" < '2014-03-19 10:23:18.671039' OR 
           "issues"."updated_at" = '2014-03-19 10:23:18.671039' AND 
           "issues"."id" < 9)))   
ORDER BY 
  "issues"."priority"='high' DESC, 
  "issues"."priority"='medium' DESC, 
  "issues"."priority"='low' DESC, 
  (votes - suspicious_votes) DESC, 
  "issues"."updated_at" DESC, 
  "issues"."id" DESC
LIMIT 1

1 个答案:

答案 0 :(得分:4)

我找到了另一种方法,它使用SQL '92标准(Predicates 209)中的构造,行值构造函数比较谓词:

  

让Rx和Ry成为比较谓词的两个行值构造函数,并让RXi和RYi成为第i个行值构造函数元素 comp op Ry&#34;是真实,虚假或未知如下:

     
      
  • &#34; x = Ry&#34;当且仅当RXi = RYi为所有i时才为真。
  •   
  • &#34; x&lt;&gt; RY&#34;当且仅当RXi&lt;&gt;时为真。 RYi为某些人。
  •   
  • &#34; x&lt; RY&#34;当且仅当RXi = RYi且所有i <1时才是真的。 n和RXn&lt; RYn for some n。
  •   
  • &#34; x&gt; RY&#34;当且仅当RXi = RYi且所有i <1时才是真的。 n和RXn> RYn for some n。
  •   

我在in this article找到了一个示例Markus Winand。行值构造函数比较谓词可以像这样使用:

SELECT *
  FROM sales
 WHERE (sale_date, sale_id) < (?, ?)
 ORDER BY sale_date DESC, sale_id DESC

这与此查询大致相同:

SELECT *
  FROM sales
 WHERE sale_date < ? OR (sale_date = ? AND sale_id < ?)
 ORDER BY sale_date DESC, sale_id DESC

第一个警告是,要直接使用它,所有订单组件必须在同一方向,否则需要更多的摆弄。另一个是,尽管是标准的,大多数数据库都不支持行值比较谓词(确实适用于postgres)。