1

私のプロジェクトでは、以下の T-SQL コードで課題に遭遇しました。

  1. step1 は、UserModules テーブルに親モジュールとそのサブスクライブしているユーザーを入力します。
  2. step2 は、Modules_Hierarchy テーブルで step1 のモジュールに関連付けられた子モジュールをチェックし、子モジュールを親モジュールのサブスクライブ ユーザーにマッピングすることによって、UserModules テーブルに有効なレコードを挿入します。このステップは、すべての子モジュールが見つかるまで再帰的に繰り返されます。

問題:

ステップ 2 では、WHILE ループと SELECT ステートメントが相関サブクエリを使用し、テーブル UserModules が INSERT 句と関連する SELECT 句の両方の一部であるため、パフォーマンスが低下し、LOCK エスカレーションの問題でクエリが失敗することがよくあります。

ModulesUsers テーブルの最終的なデータ サイズは 4,200 万で、今後さらに大きくなることが予想されます。

エラー メッセージ:「SQL Server データベース エンジンのインスタンスは、現在 LOCK リソースを取得できません。アクティブなユーザーが少なくなったら、ステートメントを再実行してください。データベース管理者に依頼して、このインスタンスのロックとメモリの構成を確認するか、実行時間の長いトランザクションを確認してください。」</p>

このクエリを最適化する方法、つまり問題を解決するためのステップ 2 を教えてください。

ステップ1:

INSERT INTO UserModules(ModuleID, UserID)
  SELECT ModuleID, UserID
  FROM TABLEA a
  INNER JOIN TABLEB b ON a.ID = b.ID

ステップ2:

DECLARE @cnt int
SET @cnt = 1

WHILE( @cnt > 0 )      
BEGIN      

  SET @cnt = (SELECT COUNT(DISTINCT s.moduleid)
              FROM Modules_Hirarchy s WITH (nolock), Modules t      
              WHERE s.ParentModuleId = t.ModuleId      
              ------------      
                AND NOT EXISTS       
                 (SELECT ModuleId + EndUserId 
                  FROM UserModules  r      
                  WHERE s.moduleid = r.moduleid 
                    AND t.EndUserId = r.EndUserId)
                AND s.moduleid + t.EndUserId NOT IN 
                  (SELECT CAST(ModuleId AS varchar) + EndUserId 
                   FROM UserModules ))      

  IF @cnt = 0      
    BREAK      

  INSERT INTO UserModules (ModuleId, EndUserId)      
    SELECT DISTINCT s.moduleid, t.EndUserId       
    FROM Modules_Hirarchy s WITH (nolock), UserModules  t      
    WHERE s.ParentModuleId = t.ModuleId      
      AND NOT EXISTS       
       (SELECT ModuleId + EndUserId 
        FROM UserModules  r      
        WHERE s.moduleid = r.moduleid 
          AND t.EndUserId = r.EndUserId)

END  
4

1 に答える 1

0

遊ぶためのいくつかのデータ

create table #UserModules(ModuleID int, UserID int)

create table #Modules_Hirarchy(ParentModuleID int, ChildModuleID int)

insert into #UserModules (ModuleID , UserID)
values(1,1)
,(2,1)
,(3,1)
,(4,1)
,(5,1)
,(6,2)
,(7,2)

insert into #Modules_Hirarchy(ParentModuleID , ChildModuleID )
values (null,1)
,(1,2)
,(2,3)
,(3,4)
,(3,5)
,(null,6)
,(6,7)

解像度

with cts(ModuleID, UserID,parentModule ) as 
(
select a.ModuleID, a.UserID , CAST(null as int)as parentModule --, cAST(null as int)as b
from #UserModules a join #Modules_Hirarchy  b on a.ModuleID = b.ChildModuleID 
where b.ParentModuleID is null

union all

select b.ChildModuleID as ModuleID, a.UserID, b.ParentModuleID
from cts a join #Modules_Hirarchy b 
on a.ModuleID = b.ParentModuleID

)
select *
into #RESULT
from cts

言うのは難しいです:)多くの変数に編集しますが、クエリを効率的にするためにすべきこと

  1. 列の非クラスター化インデックスを分離するModuleID ParentModuleID ChildModuleID

  2. おそらく、すべてのグループに対してクエリを実行する必要はありませんが、明示的なグループに対してのみ、アンカーステートメントで可能な限り多くのグループを除外します

    #UserModules から a.ModuleID、a.UserID 、CAST(null as int) を parentModule として選択 a Join #Modules_Hirarchy b on a.ModuleID = b.ChildModuleID ここで、b.ParentModuleID は null であり、(listOfModules) 内の a.ModuleId

  3. 列に一意のインデックスを非一意の行として追加する(ParentModuleID, ChildModuleID)と、大量の行の重複が発生する可能性があります

ParentModuleID ChildModuleID のデータ選択性に依存することを除いて、それについて多くを行うことはできません

述語が単純で、データの選択性が高い限り、大きなデータセットに対してはうまくいくと思います

于 2013-01-22T15:18:17.470 に答える