我正在尝试在旧数据库(使用现有数据&表结构(上在琥珀(使用花岗岩ORM(中构建加入查询,并想知道是否可以自定义选择选择。从查询的一部分来支持跨桌连接。
这是一个名为vehicles
的表的当前表结构:
-----------------------------------------------
| vehicleid | year | makeid | modelid |
-----------------------------------------------
| 1 | 1999 | 54 | 65 |
| 2 | 2000 | 55 | 72 |
| ... | ... | ... | ... |
-----------------------------------------------
等。
其中makeid
和modelid
是makes
和models
表的外键参考。在这些表中是命名列(分别为makename
和modelname
(。
我正在尝试生成一个加入查询以吸引名称:
SELECT vehicle.yearid, make.makename AS make, model.modelname AS model FROM vehicles JOIN....
(删除加入详细信息(。
因此,当查询返回时,我有一个Vehicle
对象,并且可以访问:
Vehicle.yearid
,
Vehicle.make
和
Vehicle.model
使用花岗岩可以吗?
我可以通过使用RAW SQL获得查询的加入部分以生成,但是我不知道如何自定义表&选择部分中的列名。我尝试过创建一个对象:
class Vehicle < Granite::ORM::Base
adapter pg
primary vehicleid : Int32
field yearid : Int32
field make : String
field model : String
end
但是花岗岩正在生成以下SQL:
SELECT vehicle.yearid, vehicle.make, vehicle.model FROM vehicle JOIN...
这是因为vehicle.make
和vehicle.model
实际上存在错误。
我想要的是这个SQL:
SELECT vehicle.yearid, make.makename AS make, model.modelname AS model FROM vehicles JOIN....
有没有办法做这项工作?
根据这个问题,花岗岩尚未具有一对一的关系,但作者提到使用has_many
宏来定义一种调用该方法来调用该方法宏定义的方法,但返回该方法返回的数组中的第一个元素(因为它只能是一个元素(。
首先,您需要为另外两个表model
和make
创建模型:
class Model < Granite::ORM::Base
adapter pg
belongs_to :vehicle
primary modelid : Int32
field modelname : String
end
class Make < Granite::ORM::Base
adapter pg
belongs_to :vehicle
primary makeid : Int32
field makename : String
end
如果您的字段不仅仅是modelname
或makename
,请务必添加这些字段。
最后,您需要在原始Vehicle
类中添加has_many
关系,并定义make
和model
方法:
class Vehicle < Granite::ORM::Base
adapter pg
primary vehicleid : Int32
field yearid : Int32
has_many :makes
has_many :models
def make
makes.first["makename"]
end
def model
models.first["modelname"]
end
end
查询就像:
一样简单vehicle = Vehicle.find 2
puts vehicle.model
不幸的是,我不相信花岗岩尚未支持列别名(AS
(,而无需完全绕过ORM,因此您必须明确返回这些列(上面的代码所做的(或直接使用vehicle.model["modelname"]
。
注意:我可能会发现花岗岩返回的哈希的类型,因为它们的源代码没有任何类型的注释,并且完全依赖Crystal的类型推理,这使得很难导航。但是我认为这是{} of String => DB::Any
,但我可能错了。如果收到编译器错误,请尝试使用Symbol
而不是String
。
感谢@svenskunganka给了我一个想法,我想到了这条路线,我想出了一个靠近RAW SQL的花岗岩精神的解决方案对象。
我在模型定义中添加了sql
类方法,该方法几乎与all
相同,但剥离了更多的结构。为了支持它,我还必须在PG适配器中添加新的query
方法,但现在适用于我的用例。这是猴子绘制的代码:
class Granite::Adapter::Pg < Granite::Adapter::Base
def query(statement = "", params = [] of DB::Any, &block)
statement = _ensure_clause_template(statement)
log statement, params
open do |db|
db.query statement, params do |rs|
yield rs
end
end
end
end
module Granite::ORM::Querying
def sql(clause = "", params = [] of DB::Any)
rows = [] of self
@@adapter.query(clause, params) do |results|
results.each do
rows << from_sql(results)
end
end
return rows
end
end
有点丑
vehicles = Vehicle.sql("SELECT vehicle.vehicleid, vehicle.yearid, make.makename AS make,
model.modelname AS model FROM vehicle JOIN ...<snip>")
我可以做类似的事情:
vehicles.each do |v|
puts "#{v.yearid} #{v.make} #{v.model}"
end
它的工作原理。