Dapper Guid 数组 IN 子句 MySql

问题描述 投票:0回答:1

我想建立一个 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
c# mysql dapper
1个回答
0
投票

一个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" })

但这有点蹩脚。

© www.soinside.com 2019 - 2024. All rights reserved.