2012-01-03 165 views
3

我有一些在LLBL慢預取的查詢。下面是生成的SQL的一個簡化版本:緩慢的子查詢IN子句?

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

+1

我知道你說這是簡化的,但可能在內部和外部查詢之間有重複的別名。如果這樣做,那麼引擎可能會嘗試將其作爲相關子查詢來執行,而這可能會慢得多。 – JNK 2012-01-03 21:04:09

+0

他們沒有別名,但也許直接引用表名是做同樣的事情。我會嘗試在子查詢中添加一個別名到Table1,看看是否有幫助。 – Dan 2012-01-03 21:09:53

+0

@JNK - 這將打破*範圍*的所有規則,我無法想象這是真正的問題。 – MatBailie 2012-01-03 21:09:59

回答

0

這不是很清楚,我從讀您的查詢,你實際上想要的目的。您的外部查詢是否嘗試僅爲每個ResidentialComponentType選擇最近有效的ResidentialComponentValues記錄?

最內層查詢的DISTINCT看起來沒有必要,可能會導致數據庫在優化查詢時遇到一些困難。您只選擇了2列,並且您正在將它們分組並聚合另一列,因此我相信結果已經不同了。通過指定DISTINCT,您不會幫助數據庫更高效地執行此查詢,但查詢優化程序可能會忽略它。

類似地,第一INNER JOIN到內部查詢ResidentialComponentValues好像它是不必要的。

ON條件爲您的子查詢中的第二個INNER JOIN(如下所示)混淆了我。看起來這簡直是與ResidentialComponentValues表從你的子查詢中的第一個INNER JOIN加入您的LPA_E1結果,但我認爲你真正想要做的是從外部查詢的ResidentialComponentValues表加入。

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

我的猜測是,下面是你真正想要的查詢,但我不認爲它會產生相同的結果原件。這將只爲每個ResidentialComponentType選擇最近有效的ResidentialComponentValue記錄。

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表2列索引的列(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

我會查看您的查詢,看看我能否確定他們是否做同樣的事情。我打算完成的是爲每個ResidentialComponentType獲取具有該ResidentialComponentType最近生效日期的所有ResidentialComponentValues。我工作了一段時間,但我認爲查詢的一部分是確保ResidentialComponentType在該日期也是有效的。換句話說,我不希望那個日期後創建的。 – Dan 2012-01-03 22:22:44

+0

@丹 - 謝謝,這有助於澄清事情。我*認爲*我的答案中的第一個SELECT查詢提供了您所追求的內容。爲每個ResidentialComponentType獲取最近生效的ResidentialComponentValues,但排除僅在指定日期之後生效的任何內容。 – 2012-01-03 22:40:40

+0

他已經有一個索引'(ResidentialComponentTypeID,EffectiveDate)' – 2012-01-03 22:41:17

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-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因爲有來自ResidentialComponentValues一個Foreign Key約束它,但也許你有加入其他使用報告。


不知道怎麼會在LLBL做,但如果你可以從生成的代碼刪除任何DISTINCT的 - 特別是第一個 - 或額外的嵌套(或額外的連接),它可能會幫助困惑的優化器。

+0

我認爲LLBL正在添加獨特的,但我會檢查,看看我是否以某種方式添加它。看起來不像你的重寫有幫助。 – Dan 2012-01-03 22:17:47

+0

@Dan:你能檢查LLBL如何看待這兩個表之間的關係嗎?它是'一對多'嗎? – 2012-01-03 23:24:39