4

OK、最初に免責事項。いくつかのテーブルでエンティティ属性値アプローチを使用しています。したがって、基本的には、1 つのテーブルの 1 つの列に属性のリストがあり、それを別のビューの 1 つの行に入力したいと考えています。

私はこの解決策を見つけました、そしてそれはうまくいきます:

SQL: ソース テーブルの列値に基づく列名を持つ動的ビュー

ただし、初期ロードは非常に低速でした (514 行を入力するのに 27 分以上かかりました)。何かがおかしいと思ったので、TOP を使用して Client テーブルの一部を選択してみました。すぐに結果が出ました。この方法で、データベース全体を即座にキューに入れることができることがわかりました。しかし、私は非常に奇妙な警告を見つけました。選択できた最大数は 5250 レコードでした。

この時点まで、私はまだ即座に結果を得ていました。5251 を選択しようとすると、クエリがハングします。テストサーバーで試してみたところ、同じ制限がありましたが、数値が異なりました (最大 5321 を選択できました)。テーブルには 514 レコードしかないことに注意してください。そのため、TOP 選択に 1 つの数字を追加するとハングする理由がわかりません。誰かがこれに何か意見を持っていますか? 以下は、私の作業中のSQLクエリです。

DECLARE @cols AS NVARCHAR(MAX)
DECLARE @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(a.AttributeName) 
                from AttributeCodes a                                   
        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')


set @query = 'SELECT TOP 5250 ClientID, ' + @cols + ' from 
         (
             select c.ClientID
                    , c.[Value]
                    , a.AttributeName                                           
                from Client c
                inner join AttributeCodes a
                    on a.AttributeCodeId = c.AttributeCodeID

        )x
        pivot 
        (
            min([Value])
            for AttributeName in (' + @cols + ')
        )p'

execute(@query)

編集:

問題は、別の桁を追加することによって実行計画が完全に変更されたことにあるようです。以下に2つの結果を掲載します。なぜそれが変わるのかはまだわかりません.Inner Joinの代わりにHash Matchを使用しないようにする方法があれば.

実行計画 1 (インスタント):
実行計画 1

実行計画 2 (30 分以上): 実行計画 2

4

2 に答える 2

1

正確なインデックスの設計を確認し、選択したものとデータベースの内容のサイズと範囲を正確に把握していなければ、正確な理由を説明することはできません。

しかし、私が言えることは、SQL Server が、テーブルをスキャンすることと大量のシークを実行することのどちらが有利かについて計画を立てるために使用するコストしきい値があるということです。ここで起こっていることは、SQL Server が 5250/5251 境界でそのしきい値を超えているように見えるということです。

メイン テーブルにさらに多くのデータ行を追加し、統計を再構築してから再度クエリを実行すると、SQL Server がスキャンとハッシュを停止し、ルックアップ ベースのプランに戻る可能性があります。繰り返しシークする必要があり、もう一度低くなります。

では、この問題を解消するにはどうすればよいでしょうか。EAV は、特定のシナリオの特定のタイプの設計には適していますが、システムから大量のデータを引き出したり、これらのモデルからレポートを作成したりする場合は、EAV が問題になります。は、よく見られる問題のタイプです。厳選してるから

ここにはいくつかの解決策があります。

  1. 結合に LOOP JOINヒントを追加します。うん。
  2. データベースに求めるものをより選択してください。たとえば、一度に 1 つのクライアントの値を求めるだけです。
  3. EAV モデルを使用する理由を再評価し、従来の手法を使用してデータ モデルを再設計します。

繰り返しますが、このデザインの理由についてはあまり知りません。そのため、もし私があなたの立場にあり、背中が壁に背を向けていたとしたら、おそらく 1) と 3) の組み合わせを行うでしょう。いわば「出血を止める」というヒントを追加してから、エンティティ属性値の設計を使用するという決定を完全に再評価することを検討します.

于 2012-10-17T16:58:54.743 に答える
0

以下はフォーマットされたコメントであり、回答ではありません。

病的な好奇心から、次のテスト コードをいじっています。テストテーブルの存在をチェックするための適切なコードがないこと、乱数のエッジケースが間違いなく間違っていることを認識しています... . だからそうなるのです。

その意図は、質問で説明されているものよりもかなり多くのテスト データとより大きな結果を作成することです。示されているパラメーターを使用すると、ノートブックで初期化に約 306 秒、動的クエリを実行するのに 87 秒かかります。(Windows 7 Professional 64 ビット、16GB メモリ、SQL Server 2008 R2 64 ビット。) 問題の兆候は見られませんでした。ダニエル、明らかな違いはありますか?たとえば、Value列のデータ型が大きくなる可能性がありますか? SQL Server のバージョン? 使用可能なメモリ?私はあなたの EAV 表現を完全に台無しにしましたか?

-- Test parameters.
declare @NumberOfAttributes as Int = 1000
declare @NumberOfClients as Int = 1000
declare @NumberOfSampleRows as Int = 1000000
declare @NumberOfTopRows as Int = 10000
declare @Start as DateTime = GetDate()

-- Houseclean any prior data.
if Object_Id( 'AttributeCodes' ) is not NULL
  drop table AttributeCodes
if Object_Id( 'Client' ) is not NULL
  drop table Client

-- Create the tables.
create table AttributeCodes (
  AttributeCodeId Int Identity(1,1) not NULL,
  AttributeName VarChar(64) not NULL )
create table Client (
  ClientId Int not NULL,
  AttributeCodeId Int not NULL,
  [Value] VarChar(64) not NULL )

set nocount on

-- Generate some sample attributes.
declare @Count as Int
set @Count = @NumberOfAttributes
while ( @Count > 0 )
  begin
  insert into AttributeCodes ( AttributeName ) values
    ( 'Attr_' + Right( '000' + Cast( @Count as VarChar(8) ), 4 ) )
  set @Count = @Count - 1
  end

-- Generate some sample client data.
declare @ClientId as Int
declare @AttributeCodeId as Int
set @Count = @NumberOfSampleRows
while ( @Count > 0 )
  begin
  set @ClientId = 1 + Cast( Rand() * @NumberOfClients as Int )
  set @AttributeCodeId = 1 + Cast( Rand() * @NumberOfAttributes as Int )
  insert into Client ( ClientId, AttributeCodeId, Value ) values
    ( @ClientId, @AttributeCodeId, Replicate( 'i', Cast( Rand() * 64 as Int ) ) )
  set @Count = @Count - 1
  end

-- Build the list of columns.
declare @Cols as NVarChar(Max)
select @Cols = Stuff(
  ( select ',' + QuoteName( AttributeName ) from AttributeCodes order by AttributeName for XML path(''), type).value( '.[1]', 'NVarChar(max)' ), 1, 1, '' );

-- Build an execute the summary query.
declare @Query as NVarChar(Max)
set @Query = 'select top (' + Cast( @NumberOfTopRows as VarChar(8) ) + ') ClientId, ' + @Cols +
  ' from ( select C.ClientId, C.[Value], A.AttributeName from Client as C inner join AttributeCodes as A on A.AttributeCodeId = C.AttributeCodeId ) as X' +
  ' pivot ( Min( [Value] ) for AttributeName in (' + @cols + ') ) as P' 
declare @InitializationComplete as DateTime = GetDate()
execute( @Query )
select DateDiff( ms, @Start, @InitializationComplete ) as 'Initialization (ms)',
  DateDiff( ms, @InitializationComplete, GetDate() ) as 'Query (ms)',
  DateDiff( mi, @Start, GetDate() ) as 'Total (min)'
于 2012-10-15T01:18:40.590 に答える