2

我正在使用第 3 方 .NET 库(Rhino Security),它将它的标识符作为 guid 存储在我的 mysql 数据库中的二进制(16)字段中。一切都可以从应用程序中完美运行,但是当我尝试通过查询编辑器(mysql 的 TOAD)手动运行查询时,不会为我知道存在的标识符返回任何行。例如,如果我运行以下查询,我不会得到任何结果:

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where EntitySecurityKey  =  '02a36462-49b7-406a-a3b6-d5accd6695e5'

在没有过滤器的情况下运行相同的查询会返回许多结果,包括在 EntitySecurityKey 字段中具有上述 GUID 的结果。是否有另一种方法来编写查询以搜索 guid/二进制字段?

谢谢!

编辑

我发现 TOAD 返回一个字符串而不是一个丑陋的 blob 很有趣。使用不同的编辑器返回结果(对于未过滤的查询),我得到了原始二进制数据。我会假设我的查询可以使用 binary 关键字,但以下都不起作用:

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where EntitySecurityKey  =  BINARY '02a36462-49b7-406a-a3b6-d5accd6695e5'

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where BINARY  EntitySecurityKey  =  '02a36462-49b7-406a-a3b6-d5accd6695e5'
4

6 回答 6

3

我不熟悉 Rhino Security,但据我所知,它使用 NHibernate 存储到数据库。

.net Guid 在内部使用 1 个 Int32、2 个 Int16 和 8 个字节来存储 128 位 Guid 值。当 NHibernate (3.2) 在 BINARY(16) 列中存储/检索 Guid 值时,它分别使用 .Net ToByteArray() 方法和 Guid(Byte[] ...) 构造函数。这些方法基本上交换了 Int32 和 Int16 值的字节顺序。您可以反思方法以查看确切的代码,但这里是前 4 个字节的简单示例:

guidBytes[0] = (byte)this._int32member;
guidBytes[1] = (byte)(this._int32member >> 8), 
guidBytes[2] = (byte)(this._int32member >> 16), 
guidBytes[3] = (byte)(this._int32member >> 24);

这可能是您的问题的原因。

例如,您的 Guid 以不同的方式存储在 MySql 中。
02a36462-49b7-406a-a3b6-d5accd6695e5 - 来自 Guid.ToString() 的 Guid
6264a302-b749-6a40-a3b6-d5accd6695e5 - MySql 中的 Guid 使用 MySql hex(Guid)
注意:hex(x) 不添加破折号;为了比较,我手动添加了破折号。请注意,前 8 个字节的顺序是不同的。

要测试这是否是您的问题,请运行以下查询:

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where EntitySecurityKey = UNHEX(REPLACE(6264a302-b749-6a40-a3b6-d5accd6695e5,'-',''));  

如果是这样,我在自己的旅行中注意到的其他一些项目:

Toad for MySql 也使这一点复杂化,因为它会在 select 语句输出中将 Guid 显示为 .net 等效项。如果您将该 Guid 包含在 select 的 where 子句中(至少根据我的经验),它不会应用相同的转换。您需要使用 MySql 存储的 Guid。

您将需要在执行数据库查询时手动转换 Guid,或者您可能需要引入自定义 NHibernate 类型来以不同方式转换 Guid。

你可以参考:

更新: 我刚刚在上面的 pastebin 链接上尝试了自定义 NH 类型。字节顺序不正确,至少对于我的环境。我会需要:

private static readonly int[] ByteOrder = new[] { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };

通过 .Net 输出匹配表输出中的选择十六进制(Guid 列)。

于 2012-10-15T21:27:44.043 回答
2

令我感到有趣的是,您将 GUID 存储在binary(16)强调16的字段中。根据手册binary字段的长度以字节为单位,并将截断超过它的任何内容(尽管仅在严格模式下)。您的 GUID 是否有可能被截断?使用您的示例 GUID,02a36462-49b7-406a-a3b6-d5accd6695e5尝试使用前 16 个字符查询数据库:

WHERE EntitySecurityKey = '02a36462-49b7-40'

根据这个问题的公认答案,一个字段应该是char(16) binary存储一个 GUID,而不仅仅是binary(16). 但是,我无法在我的示例表中使用它。

我有用的是 using char(36)and binary(36)。尝试将您的字段长度更新为 36 而不是 16(为了安全起见,我首先在测试表上执行此操作)。

这是我的测试表:

CREATE TABLE test_security_keys (
    test_key1 char(16) not null,
    test_key2 char(16) binary not null,
    test_key3 char(36) not null,
    test_key4 binary(16) not null,
    test_key5 binary(36) not null
);

然后,我运行了一个脚本来插入大量 GUID(一行中的每一列都使用相同的 GUID)。您可以使用示例 GUID 对其进行测试:

INSERT INTO test_security_keys
    VALUES ('02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5');

使用 simple将显示除大小被截断SELECT * FROM test_security_keys的列之外的所有列。36另外,binary无论是否,我都能够使用常规字符串比较成功地查询列:

SELECT * FROM test_security_keys WHERE test_key3 = '02a36462-49b7-406a-a3b6-d5accd6695e5';
SELECT * FROM test_security_keys WHERE test_key5 = '02a36462-49b7-406a-a3b6-d5accd6695e5';

如果您已确认您当前的列binary(16) 没有被截断,那么我建议CAST()在您的WHERE子句中使用 a 。以下应该有效(使用您的示例查询):

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
WHERE
    EntitySecurityKey = CAST('02a36462-49b7-406a-a3b6-d5accd6695e5' AS binary(16));

如果您CAST(.. AS binary(16))和输入数据长于 16 个字节,MySQL 应该发出警告(应该不可见且不会影响任何内容),说明它必须截断数据(SHOW WARNINGS;如果您得到它们,请尝试)。这是意料之中的,但也意味着您不能用于binary(16)存储 GUID,您需要使用binary(36)or char(36)

*我没有TOAD使用.

于 2012-10-06T19:53:52.607 回答
2

当我评估其他人如何“搜索”主键为二进制类型的记录(主要是表示 GUID 的二进制(16))以将其与我自己的比较时,我遇到了这个问题。我必须承认我有点退缩,因为答案很快就偏离了用户希望基于二进制字段进行手动搜索的愿望,而是专注于将列类型本身更改为字符,在这种情况下,这是对 InnoDB 存储的谋杀.

缺少结果的尝试解决方案是搜索 Id 列而不是 EntitySecurityKey 列。鉴于 JP 的原始查询,修改后的工作版本将是:

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where Id  =  UNHEX(REPLACE('02a36462-49b7-406a-a3b6-d5accd6695e5', '-', ''));

同样,这假设 Id 字段是 binary(16) 类型。(我无法确定哪一列:Id,EntitySecurityKey 是二进制(16))。如果 EntitySecurityKey 是 binary(16) 列,则:

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where EntitySecurityKey  =  UNHEX(REPLACE('02a36462-49b7-406a-a3b6-d5accd6695e5', '-', ''));

如果根据这个问题(如何将 .NET Guid 读入 Java UUID )未能产生预期的结果,尝试相同的查询但 GUID 的前三个部分的字节序颠倒是有意义的:

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where Id  =  UNHEX(REPLACE('6264a302-b749-6a40-a3b6-d5accd6695e5', '-', ''));

我唯一能想到的另一件事是 EntitySecurityKey 是一个基于 Id 的虚拟列,但我没有足够的关于表设置的信息来验证这个语句。

干杯。

于 2015-11-09T03:55:05.803 回答
1

从您的编辑中,我对其进行了一些修改:

请试试

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
WHERE EntitySecurityKey  = X'02a3646249b7406aa3b6d5accd6695e5'

由于每个字符串都有 16 个十六进制字节,因此它可以工作......

于 2012-10-12T18:13:30.357 回答
0

您是否检查过表格中 GUID 数据的实际外观?由于它是主键,它很可能不会存储为 GUID 字符串。如果是这种情况,查询字符串值显然是行不通的。

可能是二进制格式?或者它可能是一个字符串但没有连字符?或者也许它已经以其他方式转换了?不看你的数据我不知道。但无论如何,如果在查询时没有找到您知道存在的 GUID,那么无论它采用何种格式,它显然都不是以您查询的格式存储的。

我解决这个问题的方法是在您知道存在的记录中搜索不同的值。where或者甚至只是在没有子句的情况下查询前几条记录。

这将向您展示数据的实际外观。运气好的话,这足以让您弄清楚它是如何格式化的。

如果这没有帮助,一个可能的线索可能来自这个问题:为什么二进制 Guids 与通常的表示不同

希望有帮助。

于 2012-10-06T20:06:02.260 回答
-1

不确定您的数据库是如何设置的,但我会尝试以下两种变体:

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where EntitySecurityKey  =  x'02a3646249b7406aa3b6d5accd6695e5'

SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences 
where EntitySecurityKey  =  x'6264a302b7496a40b6a3d5accd6695e5'
于 2012-10-12T13:07:12.367 回答