MySQL子查询中的IP地址编号

时间:2010-06-17 14:12:43

标签: sql mysql subquery

我的子查询存在问题,涉及存储在MySQL(MySQL 5.0)中的IPV4地址。

IP地址存储在两个表中,两个表都是网络号格式 - 例如MySQL的INET_ATON()输出的格式。第一个表('events')包含许多行与IP地址相关联的行,第二个表('network_providers')包含给定netblocks的提供者信息列表。

事件表(~4,000,000行):

event_id (int)
event_name (varchar)
ip_address (unsigned int)

network_providers 表(~60,000行):

ip_start (unsigned int)
ip_end  (unsigned int)
provider_name (varchar)

为了解决我遇到的问题而简化,目标是按照以下方式创建导出:

event_id,event_name,ip_address,provider_name

如果按照以下任一方式进行查询,我会得到我期望的结果:

SELECT provider_name FROM network_providers WHERE INET_ATON('192.168.0.1') >= network_providers.ip_start ORDER BY network_providers.ip_start DESC LIMIT 1

SELECT provider_name FROM network_providers WHERE 3232235521 >= network_providers.ip_start ORDER BY network_providers.ip_start DESC LIMIT 1

也就是说,它为我查找的任何IP返回正确的 provider_name (当然我在查询中并没有真正使用192.168.0.1)。

但是,在执行与子查询相同的查询时,按以下方式,它不会产生我期望的结果:

SELECT 
events.event_id,
events.event_name,
    (SELECT provider_name FROM network_providers 
    WHERE events.ip_address >= network_providers.ip_start 
    ORDER BY network_providers.ip_start DESC LIMIT 1) as provider
FROM events

而是返回提供商的不同(不正确)值。 提供程序列中返回的超过90%(但奇怪的是不是全部)值包含该IP的错误提供程序信息。

在子查询中使用 events.ip_address 只是为了回显该值,确认它包含我期望的值,并且子查询可以解析它。将 events.ip_address 替换为实际的网络号也可以,只需在子查询中以这种方式动态使用它,这对我不起作用。

我怀疑问题是MySQL中的子查询有一些基本和重要的东西,我没有得到。我以前在MySQL中使用过这样的IP地址,但以前没有使用子查询对它们进行查找。

问题:

我真的很感激我可以得到我想要的输出的一个例子,如果有人在这里知道,为什么我在做什么不起作用的一些启示,所以我可以避免再犯这个错误。 / p>

注意:

我正在尝试的实际实际使用情况要复杂得多(包括加入两个或三个表)。这是一个简化版本,以避免使问题过于复杂。

另外,我知道我没有在ip_start和amp;之间使用。 ip_end - 这是故意的(数据库可能已经过时,而且这种情况下数据库中的所有者几乎总是处于下一个指定的范围内,并且“最佳猜测”在这种情况下很好)但是我很感激任何改进的建议与问题有关。

效率总是很好,但在这种情况下绝对不是必要的 - 任何帮助都会受到赞赏。

2 个答案:

答案 0 :(得分:2)

你应该看一下这篇文章:

http://jcole.us/blog/archives/2007/11/24/on-efficiently-geo-referencing-ips-with-maxmind-geoip-and-mysql-gis/

在与您的查询非常类似的查询中使用IP有一些不错的想法。

您应该尝试的另一件事是使用存储的函数而不是子查询。这将简化您的查询,如下所示:

SELECT 
event.id,
event.event_name,
GET_PROVIDER_NAME(event.ip_address) as provider
FROM events

答案 1 :(得分:0)

似乎无法通过JOIN或子查询实现我想要的目标。

为了扩展 Ike Walker 关于使用存储函数的建议,我最终在MySQL中创建了一个存储函数,其中包含以下内容:

DELIMITER //
DROP FUNCTION IF EXISTS get_network_provider //
CREATE FUNCTION get_network_provider(ip_address_number INT) RETURNS VARCHAR(255)
BEGIN
DECLARE network_provider VARCHAR(255);
    SELECT provider_name INTO network_provider FROM network_providers
    WHERE ip_address_number >= network_providers.ip_start
    AND network_providers.provider_name != ""
    ORDER BY provider_name.ip_start DESC LIMIT 1;
RETURN network_provider;
END //

说明:

检查忽略空白名称,并使用> =& ip_start的ORDER BY而不是BETWEEN ip_start和ip_end是我正在使用的两个组合网络提供者数据库的特定软糖,这两者都需要以这种方式查询。

当调用函数的查询只需要返回几百个结果时,这种方法很有效(尽管可能需要几秒钟)。对于返回几千个结果的查询,可能需要2或3分钟。对于具有数万个结果(或更多)的查询,实际使用速度太慢。

使用像这样的存储函数(即每个返回的结果触发一个单独的查询)并不出乎意料,但我确实比我预期的更快地达到了性能下降。

推荐:

这样做的结果是我需要接受数据结构不适合我的需求。朋友已经向我指出了这一点,当时我并不想听到它(因为我真的很想使用那个特定的network_provider DB,因为表中的其他键对我有用,例如地理位置等。

如果您最终尝试使用任何遵循类似可疑数据格式的IP提供程序DB(或实际上任何其他数据库),那么我只能建议它们不适合并且不值得尝试将这些东西拼凑在一起,就像它们一样。

至少你需要重新格式化数据,以便可以使用简单的BETWEEN语句(没有排序,没有其他比较)可靠地使用它们,因此你可以将它与子查询(或JOINS)一起使用 - 尽管它可能是一个指出任何混乱的数据可能都不是那么可靠。