2010-06-17 43 views
2

我有一個涉及存儲在MySQL(MySQL 5.0)中的IPV4地址的子查詢問題。MySQL子查詢中的IP地址編號

IP地址存儲在兩個表格中,都是網絡號碼格式 - 例如,由MySQL的INET_ATON()輸出的格式。第一個表('events')包含許多與IP地址相關聯的行,第二個表('network_providers')包含給定網絡塊的提供者信息列表。

事件表(〜400萬行):

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

network_providers表(〜60000行):

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 

也就是說,它返回正確PROVIDER_NAME不管什麼IP我仰望(中當然我在我的查詢中並沒有真正使用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的地址,但之前沒有使用子查詢對它們進行查詢。

問題:

我會很感激的,我怎麼能得到我想要的輸出的例子,如果有人知道這裏,有所悟,爲什麼我在做什麼不工作所以我可以避免再犯這個錯誤。

注:

實際的現實使用我試圖做的是要複雜得多(包括連接兩個或三個表)。這是一個簡化版本,以避免過分複雜的問題。另外,我知道我沒有在ip_start &之間使用ip_end - 這是故意的(數據庫可能過時了,這種情況下,數據庫中的所有者幾乎總是在下一個指定的範圍內,並且'最好的猜測'在這種情況下是好的),但是我很感激任何與問題有關的改進建議。

效率總是很好,但在這種情況下絕對不是必需的 - 任何幫助表示讚賞。

+0

我認爲由於你的隱式連接(也許這是錯誤的術語,但表* *在這裏被加入......),所以彈出的笛卡兒積(或其子集)已經彈出 – MvanGeest 2010-06-17 14:16:27

回答

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 
+0

+1那個URL不會'這個問題解決了我所遇到的問題,但它很有趣(而且實際上與我正在做的事情非常相關,儘管我沒有在我的問題中想過要保持簡單)。然而,你關於存儲功能的觀點,就是擊中了頭部。 – 2010-06-21 09:32:20

0

似乎沒有辦法實現我想要的JOIN或子查詢。

要在使用存儲功能的艾克沃克的建議擴大,我結束了在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 // 

說明:

支票忽略空白名稱,並使用> = & ORDER BY代替ip_start而不是BETWEEN ip_start和ip_end是我使用的兩個組合網絡提供程序數據庫的特定fudge,兩者都需要以這種方式進行查詢。

當調用該函數的查詢只需要返回幾百個結果(儘管可能需要幾秒鐘)時,此方法仍然可行。在返回幾千個結果的查詢時,可能需要2到3分鐘。對於數以萬計的結果(或更多)的查詢,它太慢而無法實際使用。

這並不意外,因爲使用這樣的存儲函數(即每個返回的結果都會觸發一個單獨的查詢),但是我的性能比我預期的要快。

建議:

這樣的結果是,我需要接受的數據結構是不適合我的需要。這已經被一位朋友指出給我了,當時我並不想聽到這個消息(因爲我真的希望使用該特定的network_provider數據庫,因爲表中的其他鍵對我很有用,例如用於地理位置的東西)。

如果您最終試圖使用任何遵循類似可疑數據格式的IP提供程序數據庫(或者其他任何數據庫),那麼我只能建議他們不適合,並且不值得嘗試把一些能夠和他們一起工作的東西拼湊在一起。

至少您需要重新格式化數據,以便它們可以通過簡單的BETWEEN語句可靠地使用(不需要排序,也不需要其他比較),因此您可以將它用於子查詢(或JOINS) - 儘管很可能一個指標,即任何數據搞砸可能不是所有可靠的。