使用Rust sqlx查询MySQL时,VARBINARY(16)列作为WHERE条件无返回行
使用Rust sqlx查询MySQL VARBINARY(16)字段的正确方式
我用Rust的sqlx库查询MySQL表,其中machine_id字段是VARBINARY(16)类型,表结构如下:
mysql> desc machine_state; +------------+-----------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-----------------+------+-----+---------+-------+ | id | binary(16) | NO | PRI | NULL | | | data | varbinary(2048) | YES | | NULL | | | machine_id | varbinary(16) | NO | | NULL | | +------------+-----------------+------+-----+---------+-------+
在MySQL命令行中,通过UNHEX函数可以正常查询到数据:
mysql> SELECT * from machine_state where machine_id = UNHEX('b25c07f2d2904704b7921173915c62ea'); +------------------------------------+------------+------------------------------------+ | id | data | machine_id | +------------------------------------+------------+------------------------------------+ | 0x00002422C9CF4D8BB8D44941D4DE66B7 | 0x0100 | 0xB25C07F2D2904704B7921173915C62EA | +------------------------------------+------------+------------------------------------+ 1 row in set (0.00 sec)
但使用sqlx尝试多种方式均返回Ok(None),无报错但无法获取记录:
// Input variable I am looking for in a binary column let machine_id = "b25c07f2d2904704b7921173915c62ea"; // Try using UNHEX with a bind variable let query_result = sqlx::query::<_>("SELECT * from machine_state where machine_id = UNHEX(?)") .bind(machine_id) .fetch_optional(database_connection_pool).await; println!("{:?}", query_result); // Try using UNHEX directly as in the command line let query_result = sqlx::query::<_>("SELECT * from machine_state where machine_id.as_bytes = UNHEX('b25c07f2d2904704b7921173915c62ea')") .fetch_optional(database_connection_pool).await; println!("{:?}", query_result); // Try binding a Vec<u8> representation of the string let bytes: Vec<u8> = machine_id.as_bytes().to_vec(); let query_result = sqlx::query::<_>("SELECT * from machine_state where machine_id = ?") .bind(bytes) .fetch_optional(database_connection_pool).await; println!("{:?}", query_result); // OUTPUT .... // Ok(None) // Ok(None) // Ok(None)
问题分析
你的尝试存在两个核心问题:
- 十六进制字符串未正确解码:第三种方式中,
machine_id.as_bytes()获取的是字符串每个字符的ASCII字节(比如"b"对应0x62,"2"对应0x32),而非十六进制字符串解码后的16字节二进制数据,和数据库中存储的VARBINARY(16)值不匹配。 - UNHEX绑定的类型问题:第一种方式中,sqlx会将字符串参数按MySQL字符串类型处理,可能因转义或类型转换导致
UNHEX无法正确解析原始十六进制字符串。
正确解法
需要先将十六进制字符串解码为对应的二进制字节,再绑定到查询参数:
- 添加依赖:在
Cargo.toml中引入hexcrate用于十六进制解码:
[dependencies] hex = "0.4" sqlx = { version = "0.7", features = ["mysql", "runtime-tokio-native-tls"] } # 根据你的运行时调整sqlx的features
- 解码并查询:
use hex; // 原始十六进制字符串 let machine_id_hex = "b25c07f2d2904704b7921173915c62ea"; // 将十六进制字符串解码为二进制字节(注意处理解码错误) let machine_id_bytes = hex::decode(machine_id_hex).expect("Invalid hexadecimal string"); // 确保解码后的字节长度为16,匹配VARBINARY(16)的定义 assert_eq!(machine_id_bytes.len(), 16, "Machine ID must be 16 bytes"); // 使用sqlx查询,绑定二进制数据 let query_result = sqlx::query::<_>("SELECT * from machine_state where machine_id = ?") .bind(machine_id_bytes) .fetch_optional(database_connection_pool) .await; println!("{:?}", query_result);
如果使用sqlx的宏查询(需要启用macros feature),可以这样写:
let query_result = sqlx::query!("SELECT * from machine_state where machine_id = ?", machine_id_bytes) .fetch_optional(database_connection_pool) .await;
额外注意事项
- 必须保证解码后的字节长度为16,否则无法匹配
VARBINARY(16)字段的存储值。 - 若不想引入
hexcrate,可手动实现十六进制字符串解码,但使用成熟的库更可靠且不易出错。
内容的提问来源于stack exchange,提问作者SantaCruzDeveloper
相关产品推荐
相关产品推荐

