我想建立一个 IN
子句 Guid
的MySql查询。Guid
列表示为 binary(16)
在DB中。根据这里的文档和答案,我应该可以做一些类似于
var arrayOfGuidsFromDb = ...;
await dbconn.ExecuteAsync<T>("UPDATE ...
SET ...
WHERE SomeGuidField IN @Ids",
new { Ids = arrayOfGuidsFromDb }
我也在使用这个Guid转换器
class MySqlGuidTypeHandler : SqlMapper.TypeHandler<Guid>
{
public override void SetValue(IDbDataParameter parameter, Guid guid) => parameter.Value = guid.ToByteArray();
public override Guid Parse(object value) => new Guid((byte[])value);
}
不过MySql的问题是,它试图(默认情况下)通过重新安排GUID值的时间戳部分来优化DB中的GUID布局。我决定不改变这个行为,它对读写和条件都很好,如 WHERE SomeGuidField = @SomeGuid
但对于 IN
问题中的语句,它匹配0个结果。我可以写这个黑客来代替
guids.Select(guid => $"uuid_to_bin('{RotateToMatchInDbGuid(guid).ToString()}')")
其中我将每个guid转换为一个字符串,然后再将 string.Join(','...
为他们 IN
子句,帮助者方法。
static Guid RotateToMatchInDbGuid(Guid source)
{
Span<byte> result = stackalloc byte[16];
source.TryWriteBytes(result);
Swap(result, 0, 3);
Swap(result, 1, 2);
Swap(result, 4, 5);
Swap(result, 6, 7);
return new Guid(result);
}
很明显,这看起来和感觉都不对。是我做错了什么,还是缺少了一些设置,我应该启用这些设置来使Dapper的行为与 =
和 IN
GUID条件?
完整的代码。
Guid[] guids = await dbConn.QueryAsync("SELECT Id FROM SomeTable"); //returns 1 row
// query using IN clause and array param:
var usingIn = await dbConn.QueryAsync("SELECT * From SomeTable WHERE Id IN @Ids", new { Ids = guids}); // returns 0 rows, should be 1
// now query using the `=` operator and same param but as a single value
var usingEquals = await dbConn.QueryAsync("SELECT * From SomeTable WHERE Id = @Id", new { Id = guids.First() }); // returns 1 row as expected
// query using array as CSV and no params
var usingCSV = await dbConn.QueryAsync($"SELECT * From SomeTable WHERE Id IN ({BuildCsv(guids)})"); // also returns 1 row as expected
一个IN子句的参数化要求每个IN'd值有一个参数,你不能把一个CSV字符串传递给一个单一参数IN,并希望它能解决这个问题。
//no
Query("SELECT * FROM person WHERE name IN @names", new { names = "John,Jane,Mary" })
//yes
Query("SELECT * FROM person WHERE name IN (@name1,@name2,@name3)", new { name1 = "John", name2="Jane", name3="Mary" })
第一种形式只能找到名字是 "John,Jane,Mary "的人。
你也许可以用FIND IN SET。
Query("SELECT * FROM person WHERE FIND_IN_SET(name, @names) > 0", new { names = "John,Jane,Mary" })
但这有点蹩脚。