Помогите мне оптимизировать этот запрос SQL Server 2005 - PullRequest
0 голосов
/ 17 марта 2011

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

Я хочу идентифицировать клиентов (но используя только часть их адреса - размещение домашних хозяйств и предприятий), которые впервые зарегистрировали покупку в 2006 году.

Моя первая попытка была:

select
    distinct a.line1 + '|' + substring(a.zip,1,5)
from 
    registrations r
    join customers c on r.custID = c.id
    join addresses a on c.addressID = a.id
where year(r.purchaseDate) = 2006
    and a.line1 + '|' + substring(a.zip,1,5) not in (
        select
            distinct a.line1 + '|' + substring(a.zip,1,5)
        from
            registrations r
            join customers c on r.custID = c.id
            join addresses a on c.addressID = a.id
        where
            year(r.purchaseDate) < 2006
    )

, и когда он работал слишком долго, я переключал NOT EXISTS (с которым я менее удобен, но желаю экспериментировать) как

select
    distinct a.line1 + '|' + substring(a.zip,1,5)
from
    registrations r
    join customers c on r.custID = c.id
    join addresses a on c.addressID = a.id
where
    year(r.purchaseDate) = 2006
    and not exists (
        select
            1
        from
            registrations r
            join customers c on r.custID = c.id
            join addresses ia on c.addressID = ia.id
        where
            ia.line1 + '|' + substring(ia.zip,1,5) = a.line1 + '|' + substring(a.zip,1,5) and
            year(r.purchaseDate) < 2006
        )
group by
    a.line1 + '|' + substring(a.zip,1,5)

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

Итак, диагностическая информация.Возможно, вам все равно, но на всякий случай: он работает на сервере G6 с четырьмя четырехъядерными процессорами и 20 ГБ ОЗУ;каждый запрос ограничен четырьмя процессорами, чтобы поддерживать производительность запросов на веб-сервере;когда я выполняю этот запрос, мы удаляем другие крупные операции импорта и отчеты из-за взаимоблокировок, но веб-сервер ориентирован на клиентов и не может быть остановлен.) Примерно: 15 миллионов регистраций, 11 миллионов клиентов и 8,6миллион адресов.Я перестроил все индексы, чтобы быть уверенным, что проблема не в фрагментации.Тем не менее, я не совсем уверен, как правильно индексировать, поэтому я полностью готов к тому, чтобы это было проблемой - некоторые из этих индексов были результатом моего суеты, а некоторые были сценариями, которые мне дал один из инструментов анализа MSулучшить производительность.Я также не совсем уверен, как донести до вас информацию об индексе, поэтому я просто дам сценарии создания:

ALTER TABLE [dbo].[registrations] ADD  CONSTRAINT [PK_flatRegistrations_1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

.

ALTER TABLE [dbo].[customers] ADD  CONSTRAINT [PK_flatCustomers_1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

.

ALTER TABLE [dbo].[addresses] ADD  CONSTRAINT [PK_addresses] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


CREATE NONCLUSTERED INDEX [addresses] ON [dbo].[addresses] 
(
    [line1] ASC,
    [line2] ASC,
    [city] ASC,
    [state] ASC,
    [zip] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


CREATE NONCLUSTERED INDEX [deliverable] ON [dbo].[addresses] 
(
    [addressDeliverable] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


CREATE NONCLUSTERED INDEX [_dta_index_addresses_5_1543676547__K9_K1_6] ON [dbo].[addresses] 
(
    [addressDeliverable] ASC,
    [ID] ASC
)
INCLUDE ( [zip]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


CREATE NONCLUSTERED INDEX [_dta_index_addresses_5_1543676547__K1_K9_6] ON [dbo].[addresses] 
(
    [ID] ASC,
    [addressDeliverable] ASC
)
INCLUDE ( [zip]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


CREATE NONCLUSTERED INDEX [_dta_index_addresses_5_1543676547__K1_6] ON [dbo].[addresses] 
(
    [ID] ASC
)
INCLUDE ( [zip]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Спасибо большое за ваше время!

Ответы [ 4 ]

1 голос
/ 17 марта 2011

Я думаю, что псевдоним таблицы вашего подзапроса Not Exists неверен.Попробуйте это:

select  r.custID,
        a.line1 + '|' + substring(a.zip,1,5) 
from    registrations r     
join    customers c on r.custID = c.id     
join    addresses a on c.addressID = a.id 
where   r.purchaseDate between '2006-01-01' and '2006-12-31'      
and not exists (         
        select  1         
        from   registrations ir             
        join customers ic on ir.custID = ic.id           
        join addresses ia on ic.addressID = ia.id         
        where   ia.line1 = a.line1
        and     substring(ia.zip,1,5) = substring(a.zip,1,5) 
        and     ir.purchaseDate < '2006-12-31'        
        )
1 голос
/ 17 марта 2011

Моя первая попытка будет заменить:

year(r.purchaseDate) = 2006

с:

r.purchaseDate BETWEEN '2006-01-01' and '2006-12-31 23:59:59' 

, а также year(r.purchaseDate) < 2006 с r.purchaseDate < '2006-01-01'.

и убедитесь, что на purchaseDate есть индекс.

Следующая вещь (если у вас достаточно ресурсов для ее запуска):

 -- create temporary table to prepare data
CREATE TABLE #addrs (yearr int, pattern varchar(100)) -- depends on a.line1 length

-- calculate all patterns for purchase before 1st Jan 2007
INSERT INTO 
  #addrs (yearr, pattern)
SELECT
  YEAR(r.purchaseDate),
  a.line1 + '|' + substring(a.zip,1,5)
from
    registrations r
    join customers c on r.custID = c.id
    join addresses a on c.addressID = a.id
where
    r.purchaseDate < `2007-01-01`

-- optionally, but could be useful in query below
CREATE INDEX idx_temp ON #addrs (pattern, yearr)  

-- original query rewritten
SELECT
  DISTINCT pattern
FROM
  #addrs a
WHERE
  a.yearr = 2006
  and not exists (
    select top 1 1 
    from 
       #addrs aa 
    where 
       aa.pattern = a.pattern
       and aa.yearr < 2006
  )

Второе решение может иметь некоторые опечатки и не может скомпилироваться с первой попытки. Это просто идея.

0 голосов
/ 17 марта 2011

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

Если вы не можете сделать это, и это, вероятно, будет выполняться более одного раза, то вам нужно сохранить столбец в таблице адресов с

line1 + '|' + substring(zip,1,5)

Это избавляет вас от необходимости вычислять его на лету.

0 голосов
/ 17 марта 2011

Подстрока (A.zip, 1, 5) должна вызывать сканирование таблицы.Это один раз запрос?Если это так, получите результат запроса ниже и сохраните его в новой таблице.Создайте индексы для AddressToCompare и PurchaseDate и выполните свой следующий запрос к новой таблице.

...