我需要一个消息列表,其中每个消息都是当前用户与其他用户之间“对话”中的最新消息。
描述了相同的查询in this question
我到目前为止的代码是:
t1 = Arel::Table.new(:messages, :as => 't1')
t2 = Arel::Table.new(:messages, :as => 't2')
convs1 = t1.
project(
t1[:receiver_user_id].as('other_user_id'),
t1[:receiver_user_id].as('receiver_user_id'),
t1[:sender_user_id].as('sender_user_id'),
t1[:created_at].as('created_at')
).
where(t1[:sender_user_id].eq(user.id))
convs2 = t2.project(
t2[:sender_user_id].as('other_user_id'),
t2[:receiver_user_id].as('receiver_user_id'),
t2[:sender_user_id].as('sender_user_id'),
t2[:created_at].as('created_at')
).
where(t2[:receiver_user_id].eq(user.id))
conv = convs1.union(convs2)
首先,我收到一个错误:
ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check \
the manual that corresponds to your MySQL server version for the right syntax to use near \
'UNION SELECT `t2`...
如果我在下面生成的sql中手动将“UNION”替换为“UNION ALL”,则此方法有效。 上面代码中的conv.to_sql产生:
SELECT `t1`.`receiver_user_id` AS other_user_id,
`t1`.`receiver_user_id` AS receiver_user_id, `
t1`.`sender_user_id` AS sender_user_id,
`t1`.`created_at` AS created_at
FROM `messages` `t1`
WHERE `t1`.`sender_user_id` = 50
UNION
SELECT `t2`.`sender_user_id` AS other_user_id,
`t2`.`receiver_user_id` AS receiver_user_id,
`t2`.`sender_user_id` AS sender_user_id,
`t2`.`created_at` AS created_at
FROM `messages` `t2`
WHERE `t2`.`receiver_user_id` = 50
知道为什么会出现MySQL UNION错误。它是一个arel bug吗? 其次,非常感谢任何有关完成查询的帮助。
更新 使用Arel :: Nodes :: Union.new工作
答案 0 :(得分:3)
我认为这可能是一个mysql错误,这是一个mySQL错误文本。 here中讨论了类似的问题,但不完全是这个问题。
尝试迁移到另一个sql server,然后再次检查,或union all
是否有效,然后使用this:
conv = convs1.union(convs2, :all)
基于文档。
答案 1 :(得分:0)
问题实际上是sql中的括号。如果我跑,它会起作用:
Message.find_by_sql conv.to_sql.delete('()')
删除了前导"("和尾随")"
很奇怪..我不知道如何将其链接以完成查询。 (Arel :: Nodes :: Union没有组方法)。这是Rails 3.1.4
答案 2 :(得分:0)
我有类似的问题并按如下方式解决:
def last_messages
Message.find_by_sql("
SELECT messages.*,
(IF(recipient_id = #{id}, 0,1)) as outlast,
users.avatar_name,
users.name
FROM messages
INNER JOIN users
ON users.id=(IF(recipient_id = #{id}, sender_id,recipient_id))
WHERE messages.id IN
( SELECT max(id)
FROM messages
WHERE recipient_id = #{id} OR sender_id = #{id}
GROUP BY (IF(recipient_id = #{id}, sender_id, recipient_id))
)
ORDER BY messages.id DESC")
end
答案 3 :(得分:0)
这是我在最后使用的代码
all_msgs = Message.where("messages.sender_user_id = ? OR messages.receiver_user_id = ?",
user.id, user.id)
msg_ids = all_msgs.select("sender_user_id, receiver_user_id, max(id) as max_id")
.group(:sender_user_id, :receiver_user_id).map { |m| m.max_id }
all_msgs = all_msgs.where(:id => msg_ids)