gle*_*ebm 8 ruby activerecord ruby-on-rails
例如,您有一个按优先级排序的项目列表.你有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<...>
Run Code Online (Sandbox Code Playgroud)
我刚刚在这里重新发明了轮子吗?我对在后端执行此操作的其他方法非常感兴趣.
在内部,这个gem构建一个依赖于当前记录的订单值的查询,如下所示:
SELECT ... WHERE
x0 OR
y0 AND (x1 OR
y1 AND (x2 OR
y2 AND ...))
ORDER BY ...
LIMIT 1
Run Code Online (Sandbox Code Playgroud)
凡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
Run Code Online (Sandbox Code Playgroud)
我找到了一种替代方法,它使用SQL '92标准(谓词 209)中的构造,即行值构造函数比较谓词:
令 Rx 和 Ry 为比较谓词的两个行值构造函数,并令 RXi 和 RYi 分别为 Rx 和 Ry 的第 i行值构造函数元素。“Rx comp op Ry”为真、假或未知,如下所示:
- 当且仅当所有 i 的 RXi = RYi 时,“x = Ry”为真。
- 当且仅当某个 i 的 RXi <> RYi 时,“x <> Ry”为真。
- 当且仅当 RXi = RYi(对于所有 i < n)且 RXn < RYn(对于某些 n),“x < Ry”为真。
- 当且仅当 RXi = RYi(对于所有 i < n)且 RXn > RYn(对于某些 n),“x > Ry”为真。
我在Markus Winand的这篇文章中找到了一个示例。行值构造函数比较谓词可以像这样使用:
SELECT *
FROM sales
WHERE (sale_date, sale_id) < (?, ?)
ORDER BY sale_date DESC, sale_id DESC
Run Code Online (Sandbox Code Playgroud)
这大致相当于这个查询:
SELECT *
FROM sales
WHERE sale_date < ? OR (sale_date = ? AND sale_id < ?)
ORDER BY sale_date DESC, sale_id DESC
Run Code Online (Sandbox Code Playgroud)
第一个警告是,要直接使用它,所有订单组件必须处于同一方向,否则需要更多的摆弄。另一个是,尽管行值比较谓词是标准的,但大多数数据库都不支持行值比较谓词(在 postgres 上有效)。