问题描述
我现在正在努力查询表的可用列的子集并在其中包含计算几个小时。我知道这不是在 select 查询中执行计算的最佳方式,但就目前而言,我只是在研究原型,应该可行。
我在后端实现中使用 diesel-rs
作为我所有数据库操作的 ORM。数据将存储在 Postgressql 服务器中。存储在数据库中的完整表是使用以下查询创建的:
CREATE TABLE airports
(
id SERIAL PRIMARY KEY,icao_code VARCHAR(4) NOT NULL UNIQUE,-- the official ICAO code of the airport
last_update TIMESTAMP NOT NULL,-- when were the information updated the last time?
country VARCHAR(2) NOT NULL,-- two letter country code
longitude REAL NOT NULL,-- with 6 decimal places
latitude REAL NOT NULL,-- with 6 decimal places
name VARCHAR NOT NULL -- just a human readable name of the airport
);
运行 diesel migrations run
会生成 airports
表定义并且查询数据库没有任何问题。
现在我正在尝试使用相应的坐标以及到所提供坐标的距离来查询所有机场的列表(它们的 ICAO 代码)。因此,我自己创建了以下 diesel-rs
table!
宏
table! {
airport_by_distance (icao_code) {
icao_code -> Varchar,longitude -> Float8,latitude -> Float8,distance -> Float8,}
}
以及对应于 diesel-rs
定义的结构体:
#[derive(QueryableByName)]
#[table_name = "airport_by_distance"]
struct AirportBydistance {
icao_code: String,longitude: f64,latitude: f64,distance: f64,}
以下剪辑 - 据我所知 - 应该查询所需的信息:
use diesel::dsl::sql_query;
let latitude = 4.000001;
let longitude = 47.000001;
let query_sql = format!("SELECT icao_code,longitude,latitude,(3959.0 * acos(cos(radians({lat})) * cos(radians(latitude)) * cos(radians(longitude) - radians({long})) + sin(radians({lat})) * sin(radians(latitude)))) AS distance FROM airports ORDER BY distance;",lat=latitude,long=longitude);
let result = match sql_query(query_sql).load::<AirportBydistance>(database_connection) {
Ok(result) => result,Err(error) => {
error!("{:?}",error);
return Err(());
}
};
不幸的是,执行 load
方法会导致 DeserializationError(Custom { kind: UnexpectedEof,error: "Failed to fill whole buffer" })
错误。
执行的查询是:
SELECT icao_code,(3959.0 * acos(cos(radians(4.000001)) * cos(radians(latitude)) * cos(radians(longitude) - radians(47.000001)) +
sin(radians(4.000001)) * sin(radians(latitude)))) AS distance
FROM airports
ORDER BY distance;
我拿了它并手动执行了它,但它完美无缺。我什至尝试删除计算并只选择列的子集,但也没有运气。
现在我不确定我做错了什么。我该如何解决这个问题?
使用固定代码编辑:对于那些对使用 Rasmus 的有用建议后如何编码感兴趣的人:
table!
宏完全消失,数据定义结构如下所示:
#[derive(Queryable)]
struct AirportBydistance {
icao_code: String,longitude: f32,latitude: f32,}
let result = match airports.select(
(
icao_code,sql::<Double>(
&format!("(3959.0 * acos(cos(radians({lat})) * cos(radians(latitude)) * cos(radians(longitude) - radians({long})) + sin(radians({lat})) * sin(radians(latitude)))) AS distance",lat=latitude_reference,long=longitude_reference)
)
)
).load::<AirportBydistance>(database_connection)
{
Ok(result) => result,error);
return Err(());
}
};
for airport in result {
info!(
"AIRPORT: {} has {}nm distance",airport.icao_code,airport.distance
);
}
解决方法
我认为问题在于反序列化器不知道被查询列的原始类型。
尽量使用类型化的柴油名称/值,并仅在需要时使用显式 sql 字符串。而且我认为“假”表声明 airports_by_distance
没有帮助。也许是这样的:
use diesel::sql_types::Double;
let result = a::airports
.select((
a::icao_code,a::longitude,a::latitude,sql::<Double>(&format!("(3959.0 * acos(cos(radians({lat})) * cos(radians(latitude)) * cos(radians(longitude) - radians({long})) + sin(radians({lat})) * sin(radians(latitude)))) AS distance",lat=latitude,long=longitue)
))
.load::<AirportByDistance>(&db)?
(手动使用 table!
宏基本上只是告诉柴油在运行程序时实际数据库中将存在这样的表。如果不是这样,您将得到运行时错误。)