Оптимизация запросов T-SQL - PullRequest
       33

Оптимизация запросов T-SQL

1 голос
/ 04 декабря 2008

Я работаю над некоторыми обновлениями внутренней системы веб-аналитики, которую мы предоставляем нашим клиентам (в отсутствие предпочтительного поставщика или Google Analytics), и я работаю над следующим запросом:

select 
    path as EntryPage, 
    count(Path) as [Count] 
from 
    (
        /* Sub-query 1 */
        select 
            pv2.path
        from 
            pageviews pv2 
                inner join
                    (
                        /* Sub-query 2 */
                        select
                            pv1.sessionid,
                            min(pv1.created) as created
                        from
                            pageviews pv1 
                                inner join Sessions s1 on pv1.SessionID = s1.SessionID
                                inner join Visitors v1 on s1.VisitorID = v1.VisitorID
                        where
                            pv1.Domain = isnull(@Domain, pv1.Domain) and
                            v1.Campaign = @Campaign
                        group by
                            pv1.sessionid
                    ) t1 on pv2.sessionid = t1.sessionid and pv2.created = t1.created
    ) t2
group by 
    Path;

Я проверил этот запрос с 2 миллионами строк в таблице PageViews, и его запуск занимает около 20 секунд. Я заметил, что в плане выполнения дважды сканируется кластеризованный индекс, и оба раза он попадает в таблицу PageViews. В столбце Создано в этой таблице есть кластеризованный индекс.

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

Для справки, цель запроса - найти первый просмотр страницы для каждого сеанса.

РЕДАКТИРОВАТЬ: После большого разочарования, несмотря на полученную здесь помощь, я не смог заставить этот запрос работать. Поэтому я решил просто сохранить ссылку на страницу входа (и теперь страницу выхода) в таблице сеансов, что позволяет мне делать следующее:

select
    pv.Path,
    count(*)
from
    PageViews pv
        inner join Sessions s on pv.SessionID = s.SessionID
            and pv.PageViewID = s.ExitPage
        inner join Visitors v on s.VisitorID = v.VisitorID
where
    (
        @Domain is null or 
        pv.Domain = @Domain
    ) and
    v.Campaign = @Campaign
group by pv.Path;

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

Редактировать Редактировать: добавление отсутствующего индекса (после очистки с прошлой ночи) уменьшило запрос до нескольких миллисекунд). Woo Hoo!

Ответы [ 6 ]

2 голосов
/ 04 декабря 2008

Для начала,

    where pv1.Domain = isnull(@Domain, pv1.Domain) 

не будет САРГ. Насколько я помню, вы не можете оптимизировать совпадение по функции.

1 голос
/ 04 декабря 2008
SELECT  
    sessionid,  
    MIN(created) AS created  
FROM  
    pageviews pv  
JOIN  
    visitors v ON pv.visitorid = v.visitorid  
WHERE  
    v.campaign = @Campaign  
GROUP BY  
    sessionid  

, так что это дает вам сеансы для кампании. Теперь давайте посмотрим, что вы делаете с этим.

ОК, это избавляет от вашей группировки:

SELECT  
    campaignid,  
    sessionid,   
    pv.path  
FROM  
    pageviews pv  
JOIN  
    visitors v ON pv.visitorid = v.visitorid  
WHERE  
    v.campaign = @Campaign  
    AND NOT EXISTS (  
        SELECT 1 FROM pageviews  
        WHERE sessionid = pv.sessionid  
        AND created < pv.created  
    )  
1 голос
/ 04 декабря 2008

Какова природа данных в этих таблицах? Считаете ли вы, что большинство данных регулярно вставляются / удаляются?

Это полная схема для таблиц? План запроса показывает другую индексацию. Изменить: Извините, просто прочитайте последнюю строку текста. Я бы предложил, если бы таблицы регулярно очищались / вставлялись, вы могли бы подумать об исключении кластеризованного индекса и использовании таблиц в качестве кучи таблиц ... просто мысль

Определенно следует поместить некластеризованные индексы в Campaign, Domain, как предложил Джон

1 голос
/ 04 декабря 2008

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

На самом деле вы пытаетесь охватить как случай, когда вы предоставляете домен, так и то, где вы этого не делаете. Вы хотите два запроса. Они могут оптимизироваться совершенно по-другому.

1 голос
/ 04 декабря 2008

Ваш внутренний запрос (pv1) потребует некластеризованного индекса в (Домене).

Второй запрос (pv2) уже может найти нужные ему строки из-за кластеризованного индекса в Created, но pv1 может возвращать столько строк, что SQL Server решит, что сканирование таблицы выполняется быстрее, чем все блокировки, которые ему понадобятся брать. Так как группы pv1 по SessionID (и, следовательно, должны упорядочиваться по SessionID), некластеризованный индекс по SessionID, Created и включая путь должен позволить выполнить соединение MERGE. Если нет, вы можете принудительно выполнить объединение с помощью команды «ВЫБРАТЬ .. ИЗ ПРОСМОТРОВ СТРАНИЦ pv2 INNER MERGE JOIN ...»

Два указанных выше индекса будут:

СОЗДАТЬ НЕКЛАСТЕРНЫЙ ИНДЕКС ncixcampaigndomain ON PageViews (домен)

СОЗДАТЬ НЕКЛАСТЕРНЫЙ ИНДЕКС ncixsessionidcreated ON PageViews (SessionID, Created) ВКЛЮЧИТЬ (путь)

1 голос
/ 04 декабря 2008

Продолжить из Доофледорфа.

Попробуйте это:

where
   (@Domain is null or pv1.Domain = @Domain) and
   v1.Campaign = @Campaign

Хорошо, у меня есть пара предложений

  1. Создать этот покрытый индекс:

     create index idx2 on [PageViews]([SessionID], Domain, Created, Path)
    
  2. Если вы можете изменить таблицу сессий, чтобы она сохраняла страницу ввода, например. EntryPageViewID вы сможете сильно оптимизировать это.

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