Медленный подзапрос в предложении? - PullRequest
3 голосов
/ 04 января 2012

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

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
)

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

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

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

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

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

Изменить для фактического запроса ...

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
)

Редактировать для создания операторов:

/****** 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

Ответы [ 2 ]

0 голосов
/ 04 января 2012

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

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2. * * * * * * * * * * * * * * * * * * * * * * * *.Вы выбираете только 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
    )

Примечание. Предполагается, что для этого запроса будет полезен индекс из двух столбцов таблицы 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 голосов
/ 04 января 2012

Внутренний подзапрос не нуждается в 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-Server распознает это и оптимизирует, но вы можете переписать, чтобы быть уверенным:

    (
        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 из сгенерированного кода - особенно первого - или дополнительное вложение (или дополнительное соединение), это, вероятно, поможет оптимизатор.

...