例如,您有一个按优先级排序的项目列表.你有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
解决方法
我找到了另一种方法,它使用了
SQL ’92标准的构造(Predicates 209),行值构造函数比较谓词:
Let Rx and Ry be the two row value constructors of the comparison predicate and let RXi and RYi be the i-th row value constructor elements of Rx and Ry,respectively. “Rx comp op Ry” is true,false,or unknown as follows:
- “x = Ry” is true if and only if RXi = RYi for all i.
- “x <> Ry” is true if and only if RXi <> RYi for some i.
- “x < Ry” is true if and only if RXi = RYi for all i < n and RXn < RYn for some n.
- “x > Ry” is true if and only if RXi = RYi for all i < n and RXn > RYn for some n.
我在Markus Winand找到了一个例子in this article.行值构造函数比较谓词可以像这样使用:
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).