提问人:Noxx 提问时间:11/12/2023 最后编辑:Noxx 更新时间:11/12/2023 访问量:42
如何基于参数左联接has_many引用的第一个上一个/下一个实例
How to left-join the first previous/next instance of a has_many reference based on a parameter
问:
使用 PostgreSQL 15.1 在 Ruby 3.2 上运行 Rails 7。
首先,我想举一个抽象的例子:
猫:
编号 | 名字 |
---|---|
1 | 米风 |
2 | 蒂芬妮 |
玩具:
编号 | cat_id | 大 |
---|---|---|
1 | 1 | 1 |
2 | 1 | 5 |
3 | 2 | 2 |
我要解决的问题是:
基于一个尚未保存的玩具及其伟大之处,我想得到一个清单,列出所有猫和它们的玩具,这些玩具略低于或大于我想要创造的玩具。
如果给一个新玩具,结果应该是:greatness=4
cat_id | toy_prev.伟大 | toy_next.伟大 |
---|---|---|
1 | 1 | 5 |
2 | 2 | 零 |
要求:
- 两个玩具都应该“左连接”,因为它们不需要存在。
- 每只猫只有一排
- 我需要完全访问模型,包括猫和两个玩具,而不仅仅是属性。
greatness
我以后喜欢使用的伪代码是这样的:
Cats.with_toys_by(4).each do |cat|
[cat.id, cat.toy_prev.greatness, cat.toy_next.greatness]
end
到目前为止,我已经尝试过:
- 最简单的解决方案是迭代并在这个循环中,调用以获取左侧和右侧。这是非常不高性能的,因为这会触发 N+2 查询。
Cat.all
@cat.toys.where("greatness < ?", 4).order(greatness: :desc).first()
@cat.toys.where("greatness > ?", 4).order(greatness: :asc).first()
- 与条件一起使用不起作用,因为这不能直接在主查询的 .
has_one
where()
joins
事情,我在想:
如果我用纯 SQL 编写它,我会在两个方向上左联接表两次:Toy
SELECT cats.*, t1.* FROM cats
-- makes sure the requirement matches
LEFT JOIN toys t1 ON (cats.id = t1.cat_id AND t1.greatness < 4)
-- filter out ones which would have toys with a greater greatness, a.k.a. get the first one
LEFT JOIN toys t2 ON (cats.id = t1.cat_id AND t1.greatness < t2.greatness)
-- same for the other side
LEFT JOIN toys t3 ON (cats.id = t3.cat_id AND t3.greatness > 4)
LEFT JOIN toys t4 ON (cats.id = t4.cat_id AND t3.greatness > t4.greatness)
WHERE
t2.id IS NULL AND t4.id IS NULL
t1
将是 ,具有较低伟大的那个 - 并且将是.toy_prev
t3
toy_next
使用将是一种选择,我还没有使用它,我不太喜欢这个想法,原因如下:Arel
Arel
是一个私有 API,不建议公开使用。- 当尝试在返回的哈希值和模型实例之间进行映射时,它似乎很难。进一步过滤或同时使用 ORM,例如 ,似乎不受支持,至少如果不仅用于 -子句。
Arel
Cats.where(..).with_toys_by(4).limit(5)
Arel
where
有没有直接使用官方ORM的“干净”解决方案来解决我的问题?
还是我需要回退到?Arel
答: 暂无答案
评论