如何选择带有柴油 rs 的列子集?

Posted

技术标签:

【中文标题】如何选择带有柴油 rs 的列子集?【英文标题】:How do I select a subset of columns with diesel-rs? 【发布时间】:2021-05-08 21:41:25 【问题描述】:

我现在正在努力几个小时来查询表的可用列的子集以及在其中包含计算。我知道这不是在选择查询中执行计算的最佳方式,但目前我只是在制作原型,它应该是可行的。

我在后端实现中使用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-rstable!

table! 
    airport_by_distance (icao_code) 
        icao_code -> Varchar,
        longitude -> Float8,
        latitude -> Float8,
        distance -> Float8,
    

以及diesel-rs定义对应的struct:

#[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,
       longitude,
       latitude,
       (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,
    distance: f64,

查询数据库的代码如下:

let result = match airports.select(
    (
        icao_code,
        longitude,
        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_reference, long=longitude_reference)
        )
    )
).load::<AirportByDistance>(database_connection)

    Ok(result) => result,
    Err(error) => 
        error!(":?", error);
        return Err(());
    
;

for airport in result 
    info!(
        "AIRPORT:  has nm distance",
        airport.icao_code, airport.distance
    );

【问题讨论】:

【参考方案1】:

我认为问题在于反序列化器不知道查询列的原始类型。

尽量使用类型化的柴油名称/值,仅在需要时使用显式 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!宏基本上只是告诉diesel在运行程序时实际数据库中会存在这样的表。如果不是这样,你会得到运行时错误。)

【讨论】:

以上是关于如何选择带有柴油 rs 的列子集?的主要内容,如果未能解决你的问题,请参考以下文章

带有过程调用的 Oracle 多列子查询

如何使用柴油进行 IN 查询?

如何在列子集上实现 PySpark StandardScaler?

s-s-rS 2008:如何立即生成多个报告?

Pyspark - 如何将函数仅应用于 DataFrame 中的列子集?

如何在data.table中使用某些列名的字符向量选择列?[重复]