2009-04-01 7 views
4

J'ai la procédure stockée suivante qui renvoie A, B et le compte dans l'ordre décroissant. J'essaie d'utiliser ROW_NUMBER, donc je peux paginer les enregistrements, mais je veux que le numéro de la première ligne 1 soit le record avec le plus grand nombre, donc fondamentalement, si je retourne une table avec 3 enregistrements et le nombre est 30, 20, 10, puis numéro de ligne 1 doit correspondre au nombre 30, le numéro de ligne 2 doit correspondre au nombre 20 et le numéro de ligne 3 doit correspondre au nombre 10. dbo.f_GetCount est une fonction qui renvoie un nombre.Comment utiliser ROW_NUMBER dans la procédure suivante?

create procedure dbo.Test 
as 
@A nvarchar(300) = NULL, 
@B nvarchar(10) = NULL 
as 

select @A = nullif(@A,'') 
     ,@B = nullif(@B,''); 

select h.A 
     ,hrl.B 
     ,dbo.f_GetCount(hrl.A,h.B) as cnt 
from dbo.hrl 
    inner join dbo.h 
     on h.C = hrl.C 
where(@A is null 
     or h.A like '%'[email protected]+'%' 
    ) 
    and (@B is null 
      or hrl.B = @B 
     ) 
group by hrl.B 
     ,h.A 
order by cnt desc; 
+0

Ajoutez un point-virgule après @B = NULLIF (@B, '') si vous souhaitez utiliser une clause WITH dans votre instruction. – Quassnoi

Répondre

6
WITH q AS 
     (
     SELECT h.A, hrl.B, 
       dbo.f_GetCount(hrl.A,h.B) as cnt 
     FROM dbo.hrl 
     INNER JOIN dbo.h on h.C = hrl.C 
     WHERE (@A IS NULL OR h.A like '%' + @A + '%') 
      AND (@B IS NULL OR hrl.B = @B) 
     GROUP BY hrl.B, h.A 
     ) 
SELECT q.*, ROW_NUMBER() OVER (ORDER BY cnt DESC) AS rn 
FROM q 
ORDER BY rn DESC 

Pour récupérer première 10 lignes, utilisez:

WITH q AS 
     (
     SELECT h.A, hrl.B, 
       dbo.f_GetCount(hrl.A,h.B) as cnt 
     FROM dbo.hrl 
     INNER JOIN dbo.h on h.C = hrl.C 
     WHERE (@A IS NULL OR h.A like '%' + @A + '%') 
      AND (@B IS NULL OR hrl.B = @B) 
     GROUP BY hrl.B, h.A 
     ) 
SELECT TOP 10 q.*, 
     ROW_NUMBER() OVER (ORDER BY cnt DESC, A, B) AS rn 
FROM q 
ORDER BY cnt DESC, A, B 

Pour récupérer les lignes entre 11 et 20, utilisez:

SELECT * 
FROM (
     WITH q AS 
       (
       SELECT h.A, hrl.B, 
         dbo.f_GetCount(hrl.A,h.B) as cnt 
       FROM dbo.hrl 
       INNER JOIN dbo.h on h.C = hrl.C 
       WHERE (@A IS NULL OR h.A like '%' + @A + '%') 
        AND (@B IS NULL OR hrl.B = @B) 
       GROUP BY hrl.B, h.A 
       ) 
     SELECT q.*, 
       ROW_NUMBER() OVER (ORDER BY cnt DESC, A, B) AS rn 
     FROM q 
     ) qq 
WHERE rn BETWEEN 11 AND 20 
ORDER BY cnt DESC, A, B 
+0

Juste curieux, à quoi exactement est le pour? Est-ce que cela améliore les performances? – Xaisoft

+0

Dans ce cas, il était plus simple de copier et coller en utilisant avec :) C'est utile pour les CTE, par ailleurs il se comporte comme une sous-requête. – Quassnoi

+0

Je vais mettre à jour ma procédure dans ma question parce qu'elle me donnait une erreur en disant que je devais mettre fin à ma déclaration, alors peut-être que vous pouvez mettre à jour votre réponse lorsque je mettrai à jour ma question. Merci pour l'instant. – Xaisoft

0
SELECT h.A, hrl.B, 
     dbo.f_GetCount(hrl.A,h.B) as cnt, 
ROW_NUMBER() over (order by cnt desc) as row_num 
FROM dbo.hrl 
INNER JOIN dbo.h on h.C = hrl.C 
WHERE (@A IS NULL OR h.A like '%' + @A + '%') 
    AND (@B IS NULL OR hrl.B = @B) 
GROUP BY hrl.B, h.A 
ORDER BY cnt desc 

Cela devrait faire l'affaire. Je n'ai pas de SSMS devant moi pour tester, mais vous devrez peut-être remplacer l'utilisation de 'cnt' dans la clause order by ROW_NUMBER par un second appel à la fonction, mais cela devrait vous donner l'idée générale.

+0

Cela me donne l'erreur: Syntaxe incorrecte près du mot-clé «plus» – Xaisoft

+0

Désolé, devrait être ROW_NUMBER() –

+0

J'ai essayé cela et il m'a donné la même erreur. – Xaisoft

3

J'utiliser une sous-requête pour obtenir les valeurs de la fonction dans le résultat, puis la fonction de classement ROW_NUMBER, comme suit:

select 
    ROW_NUMBER() over (order by t.cnt desc) as RowId, t.* 
from 
    (
     SELECT 
      h.A, hrl.B, dbo.f_GetCount(hrl.A,h.B) as cnt 
     FROM 
      dbo.hrl 
       INNER JOIN dbo.h on h.C = hrl.C 
     WHERE 
      (@A IS NULL OR h.A like '%' + @A + '%') AND 
      (@B IS NULL OR hrl.B = @B) 
     GROUP BY 
      hrl.B, h.A 
    ) as t 
order by 
    1 

Si vous vouliez seulement une certaine partie des résultats (par exemple, pour la pagination), alors vous besoin d'un autre sous-requête, puis filtrer le numéro de ligne:

select 
    t.* 
from 
    (
     select 
      ROW_NUMBER() over (order by t.cnt desc) as RowId, t.* 
     from 
      (
       SELECT 
        h.A, hrl.B, dbo.f_GetCount(hrl.A,h.B) as cnt 
       FROM 
        dbo.hrl 
         INNER JOIN dbo.h on h.C = hrl.C 
       WHERE 
        (@A IS NULL OR h.A like '%' + @A + '%') AND 
        (@B IS NULL OR hrl.B = @B) 
       GROUP BY 
        hrl.B, h.A 
      ) as t 
    ) as t 
where 
    t.RowId between 1 and 10 
order by 
    t.RowId 

Notez que dans cette requête, vous pourriez Placez ROW_NUMBER n'importe où dans la liste de sélection, car vous n'utilisez plus la syntaxe «order by 1» pour l'ordre order by.

Il y a un problème subtil ici lorsque vous appelez cette requête plusieurs fois. Il n'est pas garanti que l'ordre dans lequel les enregistrements sont renvoyés sera cohérent si le nombre d'éléments dans chaque groupe n'est pas unique. Pour résoudre ce problème, vous devez modifier la fonction ROW_NUMBER afin de commander les champs qui composent le groupe dans le compte.

Dans ce cas, il serait A et B, ce qui:

select 
    t.* 
from 
    (
     select 
      ROW_NUMBER() over (order by t.cnt desc, t.A, t.B) as RowId, t.* 
     from 
      (
       SELECT 
        h.A, hrl.B, dbo.f_GetCount(hrl.A,h.B) as cnt 
       FROM 
        dbo.hrl 
         INNER JOIN dbo.h on h.C = hrl.C 
       WHERE 
        (@A IS NULL OR h.A like '%' + @A + '%') AND 
        (@B IS NULL OR hrl.B = @B) 
       GROUP BY 
        hrl.B, h.A 
      ) as t 
    ) as t 
where 
    t.RowId between 1 and 10 
order by 
    t.RowId 

Cela finit par commander les résultats constamment entre les appels lorsque le nombre des éléments entre les groupes n'est pas unique (en supposant le même ensemble de données).

+0

pourquoi la commande par 1? – Xaisoft

+0

Jusqu'ici, le vôtre a fonctionné, mais je suis toujours curieux de connaître la commande par 1 et si je voulais retourner les lignes 1 à 10, où devrais-je mettre la clause where? – Xaisoft

+0

@Xiasoft: Pour commander par le premier champ. Il est possible que vous ayez des comptes identiques. Pour contourner cela, vous devez placer plus de champs de commande dans la section over (order by ...). Cela affectera le row_number, qui est la seule chose que vous devez commander (d'où le 1). – casperOne

Questions connexes