2012-01-03 16 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

+0

エイリアスはありませんが、テーブル名を直接参照することでも同じことが行われます。サブクエリ内のTable1にエイリアスを追加してみましょう。 – Dan

+0

@JNK - これは* scope *のすべての規則を破りますが、これは本当に問題だとは思いません。 – MatBailie

答えて

0

あなたが実際に達成しようとしていることをあなたのクエリから読み取ることは私には分かりません。あなたの外部クエリは、各ResidentialComponentTypeの最新の有効なResidentialComponentValuesレコードのみを選択しようとしていますか?最も内側のクエリにDISTINCT

は不要と思われると、クエリの最適化にデータベースにいくつかの困難を引き起こす可能性があります。2つの列を選択するだけで、1つずつグループ化して別の列を集約しているので、結果はすでに別個のものになっているはずです。おそらくクエリオプティマイザが無視するかもしれませんが、DISTINCTを指定することで、データベースがこのクエリをより効率的に実行するのを支援しているわけではありません。

同様に、内部クエリの最初のINNER JOINとResidentialComponentValuesは不要であるようです。

サブクエリの次のINNER JOINの条件は、私を混乱させます。これは単にあなたのサブクエリの最初のINNER JOINのResidentialComponentValuesテーブルと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 
    ) 

側注:私の推測では、このクエリは、列(ResidentialComponentTypeID、EffectiveDate)のためResidentialComponentValuesテーブルの上に2列のインデックスの恩恵を受けるだろうということです。


また、以下のこのクエリでは、元のものと同じ結果が得られる可能性が高いと思います。次のテストデータが与えられ

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を返す2番目のクエリは、私はあなたが言った知っている、レコード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

+0

@ダン - ありがとう、それは事を明確にするのに役立ちます。私は*私の答えの最初のSELECTクエリは、あなたがしているものを提供すると思う*。各ResidentialComponentTypeに対して最も最近有効なResidentialComponentValuesを取得しますが、指定された日付以降にのみ有効なものは除外します。 –

+0

彼はすでに '(ResidentialComponentTypeID、EffectiveDate) 'のインデックスを持っています –

0

内側のサブクエリがすでにGROUP BY ResidentialComponentTypeIDとしてDISTINCTを必要としません。

(
     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 

Foreign Key制約がResidentialComponentValuesからそこにあったとして、あなたはまた、ResidentialComponentTypesに参加する必要はありませんが、おそらくあなたは、他で使用されるように結合していることレポート。


ことがLLBLで行われるだろうが、あなたは、コード生成からDISTINCTのいずれかを削除できるかどうかは考え - 特に最初の1 - または追加のネスティング(または追加の参加)、それはおそらく助けます混乱しているオプティマイザ

+0

私はLLBLが別のものを追加していると思いますが、私は何とかそれを追加しているかどうか確認します。あなたの書き換えが助けになったようには見えません。 – Dan

+0

@ Dan:LLBLが2つのテーブルの関係をどのように見ているか確認できますか?それは「1対多」ですか? –

関連する問題