web-dev-qa-db-ja.com

カーソルベースのフィルタリングロジックをCTEベースに変換

CTE(Common Table Expressions)を使用して秒単位で実行するように減らすことができた約数分間長く実行されるカーソルがあります。カーソルの各反復で上位10個の値を取得する必要があるという条件が与えられるまで、結果セットは同一でした。これは、セットベースのロジックでは実行できません。

カーソルベースのロジックを次のように想定します。

_SET @mycurse = CURSOR FOR
SELECT value FROM sometable

OPEN @mycurse

    FETCH NEXT FROM @mycurse INTO @SomeVariable

    IF @@FETCH_STATUS <> 0 
        Print 'Error in the Cursor Fetch Statement'

    WHILE @@FETCH_STATUS = 0    

    BEGIN

INSERT INTO @TOP10      
    SELECT Value1,Value2,Value3,Value4
        FROM (
                select Value1,Value1,Value3,Value4 from BaseTable where SomeVariable=@SomeVariable
        )           

        FETCH NEXT FROM @mycurse INTO @SomeVariable

    END 

CLOSE @mycurse
_

そして、CTEベースのソリューションは次のようになります。

_;With myCTE(value)
as
(
    SELECT value FROM sometable
)
INSERT INTO @TOP10      
    SELECT Value1,Value2,Value3,Value4
    FROM 
      (select 
          Value1, Value1, Value3, Value4 
       from BaseTable BT, myCTE MC 
       where BT.SomeVariable = MC.SomeVariable)
_

どちらも正常に動作し、CTEバージョンでは大幅に少ない時間で同じ結果が返されることがわかります。しかし、上位10個の値を持つというロジックはうまくいかず、20個の値を返すことになっている10個の値のみを返します。

_SET @mycurse = CURSOR FOR
SELECT value FROM sometable

OPEN @mycurse

    FETCH NEXT FROM @mycurse INTO @SomeVariable

    IF @@FETCH_STATUS <> 0 
        Print 'Error in the Cursor Fetch Statement'

    WHILE @@FETCH_STATUS = 0    

    BEGIN

INSERT INTO @TOP10      
    SELECT top 10 Value1,Value2,Value3,Value4
    FROM 
        (select 
             Value1, Value1, Value3, Value4 
         from BaseTable 
         where SomeVariable = @SomeVariable)            

        FETCH NEXT FROM @CUR_Liability INTO @VarLiability

    END 

CLOSE @CUR_Liability
_

したがって、このシナリオの解決策を提供してください。必ずしもCTEベースである必要はありません。私の目標は実行時間を短縮することです。また、これが十分に明確でない場合は、サンプル結果セットで更新できます。前もって感謝します。

pdateRow_Number()関数を使用してソリューションを作成することができましたが、他のオプションについてはまだ興味があります。

4
Riddler

私が正しく理解していて、カーソル(またはcte)の反復ごとに2番目のテーブルの10個の値を挿入したい場合は、これを使用できます。

_; WITH myCTE (value) AS
    ( SELECT value FROM sometable )
INSERT INTO @TOP10      
    SELECT t.Value1, t.Value2, t.Value3, t.Value4
    FROM myCTE AS mc
      CROSS APPLY 
        ( SELECT TOP (10)
              bt.Value1, bt.Value2, bt.Value3, bt.Value4
          FROM BaseTable AS bt 
          WHERE bt.SomeVariable = mc.value
          ORDER BY <SomeColumns>                     -- needs something here
        ) AS t ;
_

また、一貫性を保つために、TOPを含むサブクエリに_ORDER BY_が必要です。 _order by_がない場合、データベースエンジンは条件に一致する10行を自由に返すことができます。

ROW_NUMBER()を使用して行うこともできます。

_; WITH myCTE (Value1, Value2, Value3, Value4) AS
    ( SELECT bt.Value1, bt.Value2, bt.Value3, bt.Value4,
             Rn = ROW_NUMBER() OVER (PARTITION BY bt.SomeVariable
                                     ORDER BY <SomeColumns>)
      FROM sometable AS s
        JOIN BaseTable AS bt 
          ON bt.SomeVariable = s.value
    )
INSERT INTO @TOP10      
    SELECT Value1, Value2, Value3, Value4
    FROM myCTE 
    WHERE Rn <= 10 ;
_
4
ypercubeᵀᴹ