2010-06-17 1 views
2

У меня проблема с подзапросом с адресами IPV4, хранящимися в MySQL (MySQL 5.0).Номера IP-адресов в подзапросе MySQL

IP-адреса хранятся в двух таблицах, как в формате сетевого номера - например, формат, выводимый INET_ATON() MySQL. Первая таблица («события») содержит множество строк с связанными с ними IP-адресами, вторая таблица («network_providers») содержит список информации о поставщике для данных сетевых блоков.

событие таблицы (~ 4000000 строк):

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, которые я не получаю. Ранее я работал с IP-адресами, подобными этому в MySQL, но раньше не делал поиска для них, используя подзапрос.

Вопрос:

Я действительно ценю пример того, как я мог бы получить выход, я хочу, и если кто-то здесь знает, некоторое просветление, почему то, что я делаю не работает поэтому я могу избежать повторения этой ошибки.

Примечания:

Фактическое использование в реальном мире, что я пытаюсь сделать, это значительно сложнее (с участием объединения двух или трех таблиц). Это упрощенная версия, чтобы избежать чрезмерного усложнения вопроса.

Кроме того, я знаю, что я не использую между ip_start & ip_end - это намеренно (DB может быть устаревшим, и в таких случаях владелец в базе данных почти всегда находится в следующем указанном диапазоне и «лучше угадайте, в этом контексте хорошо, однако я благодарен за любые предложения по улучшению, которые касаются вопроса.

Эффективность всегда хорошая, но в этом случае абсолютно не обязательно - любая помощь оценивается.

+0

Я думаю, что извращенный Cartesian Product (или его подмножество) появился из-за вашего неявного соединения (возможно, это неправильный термин, но таблицы * здесь соединяются здесь ...) – MvanGeest

ответ

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-адрес не " t довольно решает проблему, которую я имею, но это было интересно (и на самом деле очень важно для того, что я делаю, хотя я не занимался этим в своем вопросе, так как хотел, чтобы это было просто). Ваша точка зрения о сохраненной функции, однако, ударяет ноготь по голове. –

0

Там, кажется, нет никакого способа, чтобы добиться того, что я хотел с объединением или подзапросом.

Чтобы расширить предложение Ike Walker «s использования сохраненной функции, я в конечном итоге создание хранимой функции в 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 - это особая выдумка для двух баз данных объединенного сетевого провайдера, которые я использую, которые должны быть запрошены таким образом.

Этот подход хорошо работает, когда запрос, вызывающий функцию, должен возвращать только несколько сотен результатов (хотя может потребоваться несколько секунд). В запросах, которые возвращают несколько тысяч результатов, может потребоваться 2 или 3 минуты. Для запросов с десятками тысяч результатов (или более) это слишком медленно для практического использования.

Это не было неожиданностью при использовании хранимой функции, подобной этой (т. Е. Каждый результат, возвращаемый при запуске отдельного запроса), но я действительно упал в производительности раньше, чем я ожидал.

Рекомендация:

Результатом этого было то, что мне нужно было признать, что структура данных просто не подходит для моих потребностей. Это уже было указано мне другом, это было не то, что я действительно хотел услышать в то время (потому что я действительно хотел использовать эту конкретную базу данных network_provider из-за других ключей в таблице, которые мне были полезны, например, для таких вещей, как геолокация).

Если вы в конечном итоге пытаетесь использовать любые базы данных поставщика IP-услуг (или, как и любая другая база данных), которые следуют за аналогичным сомнительным форматом данных, то я могу только предположить, что они просто не подходят, и не стоит пытаться чтобы сплести что-то вместе, что будет работать с ними, как они есть.

По крайней мере, вам необходимо переформатировать данные, чтобы их можно было надежно использовать с помощью простого оператора BETWEEN (без сортировки и других сравнений), поэтому вы можете использовать его с подзапросами (или JOINS) - хотя это, скорее всего, индикатор того, что любые данные, которые перепутались, вероятно, не настолько надежны.