首页 > 解决方案 > 有没有办法为 Activerecord 中的单个查询创建临时列?

问题描述

假设我们有一个带有以下迁移的 ChessPiece 类。

create_table :chess_pieces do |t|
  t.string  :type
  t.integer :row
  t.integer :column
  t.integer :game_id, index: true
end

如果一个主教想要从 (0,0) 移动到 (7,7),那么我需要检查两个位置之间的每个空间是否有其他可能阻碍我的主教的棋子。IE 如果 (5,5) 上有一个 Pawn。

我可以在数据库中查询每个可能有这样一个阻塞块的位置:

pieces = []
8.times do |i|
  pieces << ChessPiece.where(game_id: @game_id, row: i, column: i)
end

但我想减少查询。或者,我可以获取一个游戏的所有棋子并在 Ruby 中迭代它们,但这似乎也效率低下。我想做的是告诉数据库将rowcolumn列组合成“行列”的形式并调用它position。这样我就可以运行如下查询:

ChessPiece.where(game_id: @game_id, position: ["1-1", "2-2", "3-3"])

如何动态创建这个新列,以便可以运行类似上述查询的内容?(假设我不能只编辑数据库模式。)

标签: ruby-on-railsrubyactiverecord

解决方案


您可以使用.select创建任何您想要的选择语句:

@chess_pieces = ChessPiece.select(
  "chess_pieces.*", 
  "CONCAT(chess_pieces.row, '-', chess_pieces.column) AS position"
)

当您使用别名时,结果中的列将作为模型的属性提供:

@chess_pieces.first.position

您还可以在某些(PG、MySQL)但不是所有 dbs 上的 WHERE、ORDER 和 GROUP 子句中使用列别名:

ChessPiece.select(
  "chess_pieces.*", 
  "CONCAT(chess_pieces.row, '-', chess_pieces.column) AS position"
).where('position = ?', '1-1')
 .group('position')
 .order('position ASC')

对于那些不支持它的人,您需要在现场进行连接、聚合或任何您正在执行的操作。

ChessPiece.order("CONCAT(chess_pieces.row, '-', chess_pieces.column)")

推荐阅读