1

私はSQLクエリを最適化しようとしていますが、常にこの1つの問題に戻り、これを最適化する方法についての洞察を得たいと思っていました。

簡潔にするために、単純な従業員テーブルがあるとします。

tbl_employees

Id     HiredDateTime
------------------
1      ...        
2      ...      

これにより、従業員ごとに別のテーブルの情報がバージョン管理されました。

tbl_emplyees_versioned

Id     Version   Name     HourlyWage
-------------------------------
1      1         Bob      10
1      2         Bob      20
1      3         Bob      30
2      1         Dan      10
2      2         Dan      20

そして、これは最新バージョンのレコードがビューで取得される方法です。

Select tbl_employees.Id, employees_LatestVersion.Name, employees_LatestVersion.HourlyWage, employees_LatestVersion.Version
From tbl_employees
Inner Join tbl_employees_versioned
 ON tbl_employees.Id = tbl_employees_versioned.Id
CROSS APPLY 
   (SELECT Id, Max(Version) AS Version
    FROM tbl_employees_versioned AS employees_LatestVersion
    WHERE Id = tbl_employees_versioned.Id
    GROUP BY Id) AS employees_LatestVersion

このような応答を取得するには:

Id     Version   Name     HourlyWage
-------------------------------
1      3         Bob      30
2      2         Dan      20

500人を超える従業員レコードがあり、それぞれにいくつかのバージョンがあるクエリをプルすると、このクエリは窒息し始め、実行に数秒かかります。

すぐにいくつかのストライキがありますが、それらを克服する方法がわかりません。

  1. 明らかに、クロスアプライはパフォーマンスの低下をもたらします。このようなバージョン管理された情報を処理する際のベストプラクティスはありますか?最高バージョンのレコードだけを取得するためのより良い方法はありますか?

  2. IdもVersionも一意ではないため、バージョン管理されたテーブルにはクラスター化インデックスがありません。連結するとそうなりますが、そのようには機能しません。代わりに、Id用の非クラスター化インデックスとVersion用の別のインデックスがあります。このテーブルにインデックスを付けてパフォーマンスを向上させるためのより良い方法はありますか?インデックス付きのビューはここで本当に役立ちますか?

4

2 に答える 2

1

データを構造化する最良の方法は、開始日と終了日を使用することだと思います。したがって、元のテーブルのデータ構造は次のようになります。

create table tbl_EmployeesHistory (
    EmployeeHistoryId int,
    EffDate date not null,
    EndDate date,
    -- Fields that describe the employee during this time
)

次に、ビューを使用して現在のバージョンを確認できます。

create view vw_Employees as
    select *
    from tbl_EmployeesHistory
    where EndDate is NULL

将来の終了日が許可されている場合、where句は次のようになります。

where coalesce(EndDate, getdate()) >= getdate()

または、この場合、EndDateをデフォルトで、「01-o1-9999」などのはるか遠くの将来の日付にすることができます。これをcreatetableステートメントのデフォルトとして追加し、列をnullにしないようにすると、いつでも次のステートメントを使用できます。

where getdate() between EffDate and EndDate

Martinがコメントで指摘しているように、coalesce()はインデックスの使用を妨げる可能性があります(SQL Serverではそうなります)が、これにはその問題はありません。

これは、ゆっくりと変化する次元と呼ばれます。Ralph Kimballは、データウェアハウジングに関する彼の著書で、この概念についてある程度詳しく説明しています。

于 2012-05-24T19:59:28.387 に答える
0

各従業員の最新バージョンを表示する方法の1つは次のとおりです。

Select Id, Name, HourlyWage, Version
FROM (
  Select E.Id, V.Name, V.HourlyWage, V.Version,
   row_number() OVER (PARTITION BY V.ID ORDER BY V.Version DESC) as nRow
  From tbl_employees E
  Inner Join tbl_employees_versioned V ON E.Id = V.Id
) A
WHERE A.nRow = 1

これにより、以前のソリューションよりもパフォーマンスが向上すると思います。tbl_employees_versionedのIdとVersionにまたがる1つのインデックスも役立つ可能性があります。

また、tbl_employees_versionedにないフィールドを選択する場合にのみ、tbl_employeesに参加する必要があることに注意してください。

于 2012-05-24T19:47:05.157 に答える