web-dev-qa-db-fra.com

Conversion de la logique filtrante à curseur à base de CTE basée sur la CTE

J'ai un curseur qui fonctionne longtemps pendant environ minutes que j'ai pu réduire pour courir en quelques secondes en utilisant un CTE (expressions de table communes). L'ensemble de résultats a été identique jusqu'à ce qu'il y ait eu une condition où je dois obtenir les 10 meilleures valeurs pour chaque itération du curseur que je ne suis pas capable de faire la logique basée sur la logique.

En supposant que la logique basée sur le curseur soit:

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

Et la mise en scène basée sur la CTE ressemble à ceci:

;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)

Au fur et à mesure que vous voyez à la fois de fonctionner et de retourner un résultat identique, avec la version CTE en utilisant considérablement moins de temps. Mais la logique d'avoir les 10 meilleures valeurs le rend faux, il ne renvoie que 10 valeurs où il est censé renvoyer 20 valeurs.

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

Alors s'il vous plaît me fournir une solution à ce scénario, il n'a pas nécessairement besoin d'être basé sur la CTE, mon objectif est de réduire le temps d'exécution. De plus, je peux mettre à jour le jeu de résultats de l'échantillon si cela n'est pas assez clair. Merci d'avance.

Mise à jour: J'ai réussi à utiliser une solution à l'aide de Row_Number() Fonction mais je suis toujours curieux d'autres options.

4
Riddler

Si je comprends bien, et que vous souhaitez insérer 10 valeurs de la 2e table pour chaque itération du curseur (ou de la CTE), vous pouvez utiliser ceci:

; 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 ;

Vous avez également besoin d'un ORDER BY Dans la sous-requête avec TOP pour être cohérent. Sans order by, Le moteur de base de données est libre de renvoyer des 10 lignes correspondant aux conditions.

Il pourrait également être fait en utilisant 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ᵀᴹ