2012-01-03 5 views
3

У меня есть несколько медленных запросов предварительной выборки в LLBL. Вот упрощенная версия сгенерированного SQL:Медленный подзапрос IN clause?

SELECT DISTINCT 
    Column1 
FROM 
    Table1 
WHERE 
Table1.Table2ID IN 
(
    SELECT Table2.Table2ID AS Table2ID 
    FROM 
     Table2 
     INNER JOIN Table1 ON Table2.Table2ID=Table1.Table2ID 
     INNER JOIN 
     (
      SELECT DISTINCT 
       Table1.Table2ID AS Table2ID, 
       MAX(Table1.EffectiveDate) AS EffectiveDate 
      FROM Table1 
      WHERE Table1.EffectiveDate <= '2012-01-03 00:00:00:000' 
      GROUP BY Table1.Table2ID 
     ) MaxEffective 
     ON 
      MaxEffective.Table2ID = Table1.Table2ID 
      AND MaxEffective.EffectiveDate = Table1.EffectiveDate 
) 

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

Я запустил советник по настройке ядра базы данных, который немного помог, но он все еще довольно медленный.

Я не очень разбираюсь в планах исполнения, но, как представляется, подавляющее большинство времени тратится на поиск индекса на Table1.

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

Если бы это был просто прямой SQL, я бы переписал запрос и сделал соединение, но я почти застрял в LLBL. Есть ли какие-либо настройки, которые я могу использовать, чтобы заставить его выполнить соединение? Есть ли причина, по которой SQL Server не создает тот же план выполнения, что и для соединения?

Edit для фактического запроса ...

SELECT DISTINCT 
    ResidentialComponentValues.ResidentialComponentValueID AS ResidentialComponentValueId, 
    ResidentialComponentValues.ResidentialComponentTypeID AS ResidentialComponentTypeId, 
    ResidentialComponentValues.Value, 
    ResidentialComponentValues.Story, 
    ResidentialComponentValues.LastUpdated, 
    ResidentialComponentValues.LastUpdatedBy, 
    ResidentialComponentValues.ConcurrencyTimestamp, 
    ResidentialComponentValues.EffectiveDate, 
    ResidentialComponentValues.DefaultQuantity 
FROM 
ResidentialComponentValues 
WHERE 
ResidentialComponentValues.ResidentialComponentTypeID IN 
(
    SELECT ResidentialComponentTypes.ResidentialComponentTypeID AS ResidentialComponentTypeId 
    FROM 
     ResidentialComponentTypes INNER JOIN ResidentialComponentValues 
     ON ResidentialComponentTypes.ResidentialComponentTypeID=ResidentialComponentValues.ResidentialComponentTypeID 
     INNER JOIN 
     (
      SELECT DISTINCT 
       ResidentialComponentValues.ResidentialComponentTypeID AS ResidentialComponentTypeId, 
       MAX(ResidentialComponentValues.EffectiveDate) AS EffectiveDate 
      FROM ResidentialComponentValues 
      WHERE ResidentialComponentValues.EffectiveDate <= '2012-01-03 00:00:00:000' 
      GROUP BY ResidentialComponentValues.ResidentialComponentTypeID 
     ) LPA_E1 
     ON 
      LPA_E1.ResidentialComponentTypeId = ResidentialComponentValues.ResidentialComponentTypeID 
      AND LPA_E1.EffectiveDate = ResidentialComponentValues.EffectiveDate 
) 

Edit для создания отчетности:

/****** Object: Table [dbo].[ResidentialComponentTypes] Script Date: 01/03/2012 13:49:06 ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
SET ANSI_PADDING ON 
GO 
CREATE TABLE [dbo].[ResidentialComponentTypes](
    [ResidentialComponentTypeID] [int] IDENTITY(1,1) NOT NULL, 
    [ComponentTypeName] [varchar](255) NOT NULL, 
    [LastUpdated] [datetime] NOT NULL, 
    [LastUpdatedBy] [varchar](50) NOT NULL, 
    [ConcurrencyTimestamp] [timestamp] NOT NULL, 
    [Active] [bit] NOT NULL, 
CONSTRAINT [PK_ResidentialComponentTypes] PRIMARY KEY CLUSTERED 
(
    [ResidentialComponentTypeID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
SET ANSI_PADDING OFF 
GO 
/****** Object: Table [dbo].[ResidentialComponentValues] Script Date: 01/03/2012 13:49:06 ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
SET ANSI_PADDING ON 
GO 
CREATE TABLE [dbo].[ResidentialComponentValues](
    [ResidentialComponentValueID] [int] IDENTITY(1,1) NOT NULL, 
    [ResidentialComponentTypeID] [int] NOT NULL, 
    [Value] [decimal](18, 3) NOT NULL, 
    [Story] [varchar](255) NOT NULL, 
    [LastUpdated] [datetime] NOT NULL, 
    [LastUpdatedBy] [varchar](50) NOT NULL, 
    [ConcurrencyTimestamp] [timestamp] NOT NULL, 
    [EffectiveDate] [datetime] NOT NULL, 
    [DefaultQuantity] [int] NOT NULL, 
CONSTRAINT [PK_ResidentialComponentPrices] PRIMARY KEY CLUSTERED 
(
    [ResidentialComponentValueID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
SET ANSI_PADDING OFF 
GO 
CREATE NONCLUSTERED INDEX [_dta_index_ResidentialComponentValues_71_56543435__K1] ON [dbo].[ResidentialComponentValues] 
(
    [ResidentialComponentValueID] 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] 
GO 
CREATE NONCLUSTERED INDEX [_dta_index_ResidentialComponentValues_71_56543435__K1_2_3_4_5_6_7_8_9] ON [dbo].[ResidentialComponentValues] 
(
    [ResidentialComponentValueID] ASC 
) 
INCLUDE ([ResidentialComponentTypeID], 
[Value], 
[Story], 
[LastUpdated], 
[LastUpdatedBy], 
[ConcurrencyTimestamp], 
[EffectiveDate], 
[DefaultQuantity]) 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] 
GO 
CREATE NONCLUSTERED INDEX [_dta_index_ResidentialComponentValues_71_56543435__K2_K1] ON [dbo].[ResidentialComponentValues] 
(
    [ResidentialComponentTypeID] ASC, 
    [ResidentialComponentValueID] 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] 
GO 
CREATE NONCLUSTERED INDEX [_dta_index_ResidentialComponentValues_71_56543435__K2_K8_K1] ON [dbo].[ResidentialComponentValues] 
(
    [ResidentialComponentTypeID] ASC, 
    [EffectiveDate] ASC, 
    [ResidentialComponentValueID] 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] 
GO 
CREATE NONCLUSTERED INDEX [_dta_index_ResidentialComponentValues_71_56543435__K2_K8_K1_3_4_5_6_7_9] ON [dbo].[ResidentialComponentValues] 
(
    [ResidentialComponentTypeID] ASC, 
    [EffectiveDate] ASC, 
    [ResidentialComponentValueID] ASC 
) 
INCLUDE ([Value], 
[Story], 
[LastUpdated], 
[LastUpdatedBy], 
[ConcurrencyTimestamp], 
[DefaultQuantity]) 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] 
GO 
/****** Object: ForeignKey [FK_ResidentialComponentValues_ResidentialComponentTypes] Script Date: 01/03/2012 13:49:06 ******/ 
ALTER TABLE [dbo].[ResidentialComponentValues] WITH CHECK ADD CONSTRAINT [FK_ResidentialComponentValues_ResidentialComponentTypes] FOREIGN KEY([ResidentialComponentTypeID]) 
REFERENCES [dbo].[ResidentialComponentTypes] ([ResidentialComponentTypeID]) 
GO 
ALTER TABLE [dbo].[ResidentialComponentValues] CHECK CONSTRAINT [FK_ResidentialComponentValues_ResidentialComponentTypes] 
GO 

enter image description here

+1

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

+0

У них нет псевдонимов, но, возможно, ссылка на имя таблицы напрямую делает то же самое. Я попробую добавить псевдоним в таблицу1 в подзапрос и посмотреть, поможет ли это. – Dan

+0

@JNK - Это нарушит все правила в * области *, я не могу себе представить, что это действительно проблема. – MatBailie

ответ

0

Мне не ясно, что вы читаете ваши вопросы, чего вы на самом деле пытаетесь достичь. Ваш внешний запрос пытается выбрать только последние эффективные записи ResidentialComponentValues ​​для каждого ResidentialComponentType?

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

Аналогичным образом, первый INNER JOIN ResidentialComponentValues ​​во внутреннем запросе кажется ненужным.

Состояние ON для вашего второго INNER JOIN в вашем подзапросе (показано ниже) меня смущает. Похоже, что это просто соединение вашего результата LPA_E1 с таблицей ResidentialComponentValues ​​с первого INNER JOIN в вашем подзапросе, но я думаю, что вы действительно пытаетесь присоединиться к нему с помощью таблицы ResidentialComponentValues ​​из внешнего запроса.

ON 
    LPA_E1.ResidentialComponentTypeId = ResidentialComponentValues.ResidentialComponentTypeID 
    AND LPA_E1.EffectiveDate = ResidentialComponentValues.EffectiveDate 

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

declare @endDate datetime 
set @endDate = '2012-01-03 00:00:00:000' 

SELECT 
    ResidentialComponentValues.ResidentialComponentValueID AS ResidentialComponentValueId, 
    ResidentialComponentValues.ResidentialComponentTypeID AS ResidentialComponentTypeId, 
    ResidentialComponentValues.Value, 
    ResidentialComponentValues.Story, 
    ResidentialComponentValues.LastUpdated, 
    ResidentialComponentValues.LastUpdatedBy, 
    ResidentialComponentValues.ConcurrencyTimestamp, 
    ResidentialComponentValues.EffectiveDate, 
    ResidentialComponentValues.DefaultQuantity 
FROM 
    ResidentialComponentValues 
WHERE 
    -- the effective date for this ResidentialComponentValue record has already passed 
    ResidentialComponentValues.EffectiveDate <= @endDate 
    -- and there does not exist any other ResidentialComponentValue record for the same ResidentialComponentType that is effective more recently 
    and not exists (
     select 1 
     from ResidentialComponentValues LPA_E1 
     where 
      LPA_E1.ResidentialComponentTypeID = ResidentialComponentValues.ResidentialComponentTypeID 
      and LPA_E1.EffectiveDate <= @endDate 
      and LPA_E1.EffectiveDate > ResidentialComponentValues.EffectiveDate 
    ) 

Side Примечание: Я думаю, что этот запрос будет извлечь выгоду из индекса в 2 колонки на столе ResidentialComponentValues ​​для колонок (ResidentialComponentTypeID, EffectiveDate).


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

SELECT 
    ResidentialComponentValues.ResidentialComponentValueID AS ResidentialComponentValueId, 
    ResidentialComponentValues.ResidentialComponentTypeID AS ResidentialComponentTypeId, 
    ResidentialComponentValues.Value, 
    ResidentialComponentValues.Story, 
    ResidentialComponentValues.LastUpdated, 
    ResidentialComponentValues.LastUpdatedBy, 
    ResidentialComponentValues.ConcurrencyTimestamp, 
    ResidentialComponentValues.EffectiveDate, 
    ResidentialComponentValues.DefaultQuantity 
FROM 
    ResidentialComponentValues 
WHERE 
    -- show any ResidentialComponentValue records where there is any other currently effective ResidentialComponentValue record for the same ResidentialComponentType 
    exists (
     select 1 
     from ResidentialComponentValues LPA_E1 
     where 
      LPA_E1.ResidentialComponentTypeID = ResidentialComponentValues.ResidentialComponentTypeID 
      and LPA_E1.EffectiveDate <= @endDate 
    ) 


Учитывая следующие тестовые данные, то первый запрос возвращает записи 2 и 4. Второй запрос возвращает записи 1, 2, 3, 4 и 5.

insert into ResidentialComponentTypes values (1) 
insert into ResidentialComponentTypes values (2) 
insert into ResidentialComponentTypes values (3) 

insert into ResidentialComponentValues (ResidentialComponentValueID, ResidentialComponentTypeID, Value, Story, LastUpdated, LastUpdatedBy, EffectiveDate, DefaultQuantity) 
      select 1, 1, 'One', 'Blah', getdate(), 'Blah', '2012-01-01', 1 
union all select 2, 1, 'Two', 'Blah', getdate(), 'Blah', '2012-01-02', 1 
union all select 3, 1, 'Three', 'Blah', getdate(), 'Blah', '2012-01-04', 1 
union all select 4, 2, 'Four', 'Blah', getdate(), 'Blah', '2012-01-02', 1 
union all select 5, 2, 'Five', 'Blah', getdate(), 'Blah', '2012-01-04', 1 
union all select 6, 3, 'Six', 'Blah', getdate(), 'Blah', '2012-01-04', 1 
+0

Я просмотрю ваши запросы и посмотрю, смогу ли я определить, делают ли они то же самое. То, что я собирался выполнить, - это получить все ResidentialComponentValues ​​для каждого ResidentialComponentType, которые имеют самую последнюю эффективную дату для этого ResidentialComponentType.Прошло некоторое время с тех пор, как я работал над этим, но я думаю, что часть запроса заключалась в том, чтобы ResidentialComponentType также был эффективен с этой даты. Другими словами, я не хочу, чтобы созданные после этой даты. – Dan

+0

@ Dan - Спасибо, это помогает прояснить ситуацию. Я * думаю *, что первый запрос SELECT в моем ответе дает вам то, что вам нужно. Получайте наиболее эффективные ResidentialComponentValues ​​для каждого ResidentialComponentType, но исключайте все, которые действуют только после указанной даты. –

+0

У него уже есть указатель на '(ResidentialComponentTypeID, EffectiveDate)' –

0

Внутренний подзапрос не нуждается в DISTINCT, как вы уже GROUP BY ResidentialComponentTypeID:

(
     SELECT DISTINCT 
      ResidentialComponentValues.ResidentialComponentTypeID 
       AS ResidentialComponentTypeId, 
      MAX(ResidentialComponentValues.EffectiveDate) 
       AS EffectiveDate 
     FROM ResidentialComponentValues 
     WHERE ResidentialComponentValues.EffectiveDate 
       <= '2012-01-03 00:00:00:000' 
     GROUP BY ResidentialComponentValues.ResidentialComponentTypeID 
    ) LPA_E1 

Не уверен, что если SQL-сервер распознает это и оптимизировать, но вы можете переписать, чтобы быть уверенным:

(
     SELECT 
      rcv.ResidentialComponentTypeID 
      MAX(rcv.EffectiveDate) AS EffectiveDate 
     FROM ResidentialComponentValues AS rcv 
     WHERE rcv.EffectiveDate 
       <= '2012-01-03 00:00:00:000' 
     GROUP BY rcv.ResidentialComponentTypeID 
    ) LPA_E1 

И если я не ошибаюсь, вы также должны ни в запросе другой DISTINCT ни добавление дополнительного подзапроса. Проверьте, если это переписывание дает те же результаты:

SELECT 
    v.ResidentialComponentValueID, 
    v.ResidentialComponentTypeID, 
    v.Value, 
    v.Story, 
    v.LastUpdated, 
    v.LastUpdatedBy, 
    v.ConcurrencyTimestamp, 
    v.EffectiveDate, 
    v.DefaultQuantity 
FROM 
     ResidentialComponentTypes AS t 
    INNER JOIN ResidentialComponentValues AS v 
     ON t.ResidentialComponentTypeID=v.ResidentialComponentTypeID 
    INNER JOIN 
     (
      SELECT 
       rcv.ResidentialComponentTypeID 
       MAX(rcv.EffectiveDate) AS EffectiveDate 
      FROM ResidentialComponentValues AS rcv 
      WHERE rcv.EffectiveDate 
        <= '2012-01-03 00:00:00:000' 
      GROUP BY rcv.ResidentialComponentTypeID 
     ) LPA_E1 
     ON 
      LPA_E1.ResidentialComponentTypeId = v.ResidentialComponentTypeID 
      AND LPA_E1.EffectiveDate = v.EffectiveDate 

Вам также не нужно, чтобы присоединиться к ResidentialComponentTypes как есть Foreign Key ограничение от ResidentialComponentValues к нему, но, возможно, вы, соединяющиеся для использования в других отчеты.


Не знаю, как это будет сделано в LLBL, но если вы можете удалить любого из DISTINCT из сгенерированного кода - особенно первый - или дополнительной вложенности (или дополнительное соединение), это, вероятно, поможет запутанный оптимизатор.

+0

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

+0

@ Dan: Вы можете проверить, как LLBL видит взаимосвязь между двумя таблицами? Это '1-ко-многим'? –