Номера IP-адресов в подзапросе MySQL - PullRequest
2 голосов
/ 17 июня 2010

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

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

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

Использование events.ip_address в подзапросе только для вывода значения подтверждает, что оно содержит ожидаемое значение и что подзапрос может его проанализировать. Замена events.ip_address на фактический номер сети также работает, просто динамически используя его в подзапросе таким способом, который мне не подходит.

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

Вопрос:

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

Примечания:

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

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

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

Ответы [ 2 ]

2 голосов
/ 17 июня 2010

Вам следует взглянуть на этот пост:

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 голосов
/ 21 июня 2010

Кажется, что нет способа достичь того, чего я хотел, с помощью 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 //

Объяснение:

Проверка игнорирования пустых имен и использование> = & ORDER BY для ip_start, а не BETWEEN ip_start и ip_end - это особая помадка для двух баз данных объединенного сетевого провайдера, которые я использую, обе из которых необходимо запрашивать в этом способ.

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...