MySQL - 如何搜索GUID

时间:2012-09-29 23:28:00

标签: mysql

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

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

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

谢谢!

修改

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

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'

6 个答案:

答案 0 :(得分:2)

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

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

根据manual的已接受答案,字段应char(16) binary来存储GUID,而不仅仅是binary(16)。但是,我无法在我的示例表中使用它。

为我工作的是char(36)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进行测试:

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');

使用简单的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 {1}}条款。以下内容应该有效(使用您的示例查询):

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)char(36)

*我没有尝试使用TOAD,但我使用了命令行MySQL Navicat并且两者都有相同的结果。

答案 1 :(得分:2)

我不熟悉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 hex(Guid)在MySql中使用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等价物。如果您在select的where子句中包含Guid(至少根据我的经验),它不会应用相同的转换。您需要使用MySql存储的Guid。

您需要在执行数据库查询时手动转换Guids,或者您可能需要引入自定义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列)。

答案 2 :(得分:1)

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

请尝试

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

由于字符串各有16个十六进制字节,因此可能有效......

答案 3 :(得分:1)

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

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

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

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

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

如果根据这个问题(How to read a .NET Guid into a Java UUID)未能产生预期的结果,那么尝试相同的查询会有意义,但GUID的前三部分的字节顺序会反转:

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

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

干杯。

答案 4 :(得分:0)

您是否检查过GUID数据在表格中的实际情况?由于它是主键,因此很可能不会存储为GUID字符串。如果是这种情况,则清楚地查询字符串值将不起作用。

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

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

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

如果这没有帮助,可能会出现一个可能的线索:Why are binary Guids different from usual representation

希望有所帮助。

答案 5 :(得分:-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'