У меня есть две таблицы, в которых я храню:
- диапазон IP - таблица соответствия стран
- список запросов с разных IP
IP-адреса были сохранены как bigint
s для улучшения производительности поиска.
Это структура таблицы:
create table [dbo].[ip2country](
[begin_ip] [varchar](15) NOT NULL,
[end_ip] [varchar](15) NOT NULL,
[begin_num] [bigint] NOT NULL,
[end_num] [bigint] NOT NULL,
[IDCountry] [int] NULL,
constraint [PK_ip2country] PRIMARY KEY CLUSTERED
(
[begin_num] ASC,
[end_num] ASC
)
)
create table Request(
Id int identity primary key,
[Date] datetime,
IP bigint,
CategoryId int
)
Я хочу получить разбивку запросов по странам, поэтому я выполняю следующий запрос:
select
ic.IDCountry,
count(r.Id) as CountryCount
from Request r
left join ip2country ic
on r.IP between ic.begin_num and ic.end_num
where r.CategoryId = 1
group by ic.IDCountry
У меня есть много записей в таблицах: около 200 000 IP2Country
и несколько миллионов Request
, поэтому запрос занимает некоторое время.
Глядя на план выполнения, самая дорогая часть - это поиск кластерного индекса по индексу PK_IP2Country, который выполняется многократно (количество строк в запросе).
Кроме того, кое-что, что я чувствую немного странно, это left join ip2country ic on r.IP between ic.begin_num and ic.end_num
часть (не знаю, есть ли лучший способ выполнить поиск).
Структура таблицы, некоторые примеры данных и запросы доступны в SQLFiddle: http://www.sqlfiddle.com/#!3/a463e/3 (к сожалению, я не думаю, что могу вставить много записей, чтобы воспроизвести проблему, но это надеюсь дает представление).
Я (очевидно) не являюсь экспертом в производительности / оптимизации SQL, поэтому мой вопрос: есть ли очевидные способы, которыми эта структура / запрос может быть улучшена с точки зрения производительности, которую мне не хватает?
источник
begin_num
. Мне также приходится присоединятьсяA BETWEEN B AND C
довольно часто, и мне любопытно узнать, есть ли способ добиться этого без утомительных присоединений RBAR.begin_ip
иend_ip
сохранении вычисляемых столбцов, чтобы не допустить какой-либо синхронизации текста и чисел.ip2country (begin_num, end_num)
?give me the first record that has a begin_num < ip in asc order of begin_num
(поправьте меня, если я ошибаюсь) могла бы быть правильной и повысить производительность.begin_num
, а затем просматриваетend_num
внутри этого набора и находит только одну запись.Ответы:
Вам нужен дополнительный индекс. В вашем примере со скрипкой я добавил:
CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)
Который охватывает вас для таблицы запросов и получает поиск по индексу вместо сканирования кластерного индекса.
Посмотрите, как это улучшит, и дайте мне знать. Я предполагаю, что это очень поможет, так как сканирование по этому индексу, я уверен, не дешево.
источник
Всегда есть подход грубой силы: вы можете взорвать свою карту IP. Соедините таблицу чисел с вашей существующей картой, чтобы создать одну запись для каждого IP-адреса. Это только 267 тысяч записей на основе ваших данных Fiddle, никаких проблем.
Это сделало бы поиск проще и, надеюсь, быстрее.
ip2country
Конечно, это имеет смысл, только если вы делаете относительно мало обновлений .Я надеюсь, что у кого-то есть лучшее решение!
источник
Попробуй это:
источник