我正在研究Ruby on Rails,并且有一些关于rails活动记录及其SQL转换的具体问题。
仅供参考,我使用的是postgresql,用户模型有很多状态,我想根据订购用户 状态的created_at 列。虽然我找到了解决方案User.includes(:statuses).order('statuses.created_at desc')
,但我仍然有一些(可能是相互关联的)我不太了解的事情。
1)在rails控制台上,(为了更好的可读性,我进行了简化)
User.joins(:statuses).to_sql
生成"SELECT users.* FROM users INNER JOIN statuses ON statuses.user_id = users.id"
。
User.includes(:statuses).references(:statuses).to_sql
生成"SELECT users.id AS t0_r0, ...(simplified)... statuses.created_at AS t1_r3 FROM users LEFT OUTER JOIN statuses ON statuses.user_id = users.id"
选择用户有什么区别。*并选择每个列?
2)也在rails控制台上,
User.joins(:statuses).size
生成SELECT COUNT(*) FROM users INNER JOIN statuses ON statuses.user_id = users.id => 155
。
User.includes(:statuses).references(:statuses).size
生成SELECT COUNT(DISTINCT users.id) FROM users LEFT OUTER JOIN statuses ON statuses.user_id = users.id => 16
。
为什么包含会自动包含 distinct 子句,而加入则不会?
3)我尝试获取 statuses.created_at 订购的不同用户,并在用户上加入状态
我使用了这个条款:User.joins(:statuses).select('users.*, statuses.created_at').order('statuses.created_at desc').distinct
。 (我应该使用 select statuses.created_at 归因于PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
)
但是这个条款不会删除重复!虽然User.joins(:statuses).select!('users.*, statuses.created_at').order('statuses.created_at desc').distinct.size
生成 16 ,但当我实际执行它时,我看到了很多重复。
它生成SQL语句:SELECT DISTINCT users.*, statuses.created_at FROM users INNER JOIN statuses ON statuses.user_id = users.id ORDER BY statuses.created_at desc
,它显示以下图像。
如您所见,它显示了我的记录的重复。
所以我的第三个问题是,为什么distinct子句不会删除重复(为什么 size 显示不同的结果)?
提前谢谢!
答案 0 :(得分:1)
joins
方法只为您生成SQL连接。任何个人用户都可以加入多种状态 - 这只是联接的内容,如果这不是您需要的,那么您可以自行处理它。 select子句默认为users.*
,这样您就不会在两个表中不知不觉地以相同名称的列相互遮蔽(例如id
列)
includes
用于加载关联。这有时通过连接完成只是一个实现细节 - 在处理结果时有额外的代码,以便结果集只为每个用户提供一次(并将状态数据输入关联)。还包括所有列名称的别名,以处理名称相同的列
最后,您的distinct子句不会删除重复项,因为行不相同 - 包含statuses.created_at
列,大多数行都不同。
size
方法会忽略您的select子句,因此会对users.id
进行计数 - 在这种情况下,distinct只会计算每个用户一次