我对Rails很新,我通过搜索找不到类似的情况。我有两个现有表event
和data
:
event
有很多列:
+-------------------+----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------------------+------+-----+---------+-------+
| sid | int(10) unsigned | NO | MUL | NULL | |
| cid | int(10) unsigned | NO | | NULL | |
| signature | varchar(255) | NO | MUL | NULL | |
| src_ip | int(10) unsigned | YES | MUL | NULL | |
| dst_ip | int(10) unsigned | YES | MUL | NULL | |
| ... | ... | ... | ... | ... | |
| etc | | | | | |
+-------------------+----------------------+------+-----+---------+-------+
data
只有三个:
+--------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+-------+
| sid | int(10) unsigned | NO | MUL | NULL | |
| cid | int(10) unsigned | NO | | NULL | |
| data_payload | text | YES | | NULL | |
+--------------+------------------+------+-----+---------+-------+
我的模特:
class Event < ActiveRecord::Base
has_one :payload, :foreign_key => 'cid', :primary_key => 'cid'
self.table_name = "event";
[more stuff]
end
class Payload < ActiveRecord::Base
belongs_to :event
self.table_name = "data";
end
使用has_one关系,我可以执行如下查询:
Event.limit(5).offset(90000).each do |e|
然后循环并为每次迭代调用e.payload
,这将执行如下查询:
SELECT `data`.* FROM `data` WHERE `data`.`cid` = 90001 LIMIT 1
但这显然不是正确的方法。我觉得我在这里输入它就是在捣蛋。
我认为我的理想查询类似于:
SELECT `event`.`cid`, `event`.`sid`, `event`.`signature`, `data`.`data_payload`
FROM `event`, `data`
WHERE `event`.`sid` = `data`.`sid` AND
`event`.`cid` = `data`.`cid` AND
`event`.`signature` LIKE 'example_signature'
有没有办法在一个查询中使用正确的Rails关联完成所有操作?只考虑我必须通过将最终的ActiveRecord / hash对象传递给我的视图的箍,让我觉得我正在以一种非常迂回的方式做这件事。
答案 0 :(得分:0)
您的意思是,它不应该在每次传递时触发新的SELECT
查询吗?
您可以使用includes()
指令对此进行优化,如下所示:
events = Event.includes(:payload).where('signature LIKE ?', 'example_signature')
events.each do |event|
puts e.payload.data_payload
end
这将在2个查询中加载所有数据,一个用于所有事件,另一个用于将外键返回到事件的所有有效负载:
Event Load (0.2ms) SELECT "event".* FROM "event" WHERE (signature LIKE 'example_signature')
Payload Load (0.2ms) SELECT "data".* FROM "data" WHERE "data"."cid" IN (2,4,5,6,7)
修改强>:
您的评论似乎表明性能是最受关注的(可能因为表格很大),所以我认为您已经非常接近了。我建议只选择你需要的列(因为事件表中有很多列),并使用和SQL JOIN
直接添加其他表的内容(因为有一对一的映射关系)事件到有效载荷)
这样就可以了:
Event.
select('event.cid, event.sid, event.signature, data.cid, data.sid, data.data_payload').
joins(:payload).
where('signature LIKE ?', 'example_signature').each do |event|
puts event.data_payload
end
# SELECT event.cid, event.sid, event.signature, data.cid, data.sid, data.data_payload FROM "event" INNER JOIN "data" ON "data"."cid" = "event"."cid" WHERE (signature LIKE 'example_signature')
这也将保留您的Event对象,因此您可以在其上调用其他方法,但如果它们不依赖于您在select()
子句中排除的属性,则仅。
如果您不需要Event对象中的任何其他方法,并且只想尽快获得结果,那么您可以使用以下命令运行原始查询:
sanitized_str = ActiveRecord::Base.connection.quote('example_signature')
result = ActiveRecord::Base.connection.execute(
"SELECT event.cid, event.sid, event.signature, data.cid, data.sid, data.data_payload
FROM event
INNER JOIN data ON data.cid = event.cid
WHERE (signature LIKE #{sanitized_str});"
)
# => [{"cid"=>2, "sid"=>1, "signature"=>"example_signature", "data_payload"=>"Hello world!", 0=>2, 1=>1, 2=>"example_signature", 3=>2, 4=>1, 5=>"Hello world!"}]
这将返回一个记录数组,每条记录都是一个普通的ruby哈希值。事件对象不会在内存中构建,从而节省了大量开销。