2009-04-01 2 views
4

У меня есть следующая хранимая процедура, которая возвращает A, B и счет в порядке убывания. Я пытаюсь использовать ROW_NUMBER, поэтому я могу записать записи, но я хочу, чтобы первая строка 1 была записью с наивысшим счетом, поэтому в основном, если я верну таблицу с 3 записями, и счет будет 30, 20, 10, тогда номер строки 1 должен соответствовать счету 30, номер строки 2 должен соответствовать счету 20, а номер строки 3 должен соответствовать счету 10. dbo.f_GetCount - функция, которая возвращает счетчик.Как использовать ROW_NUMBER в следующей процедуре?

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

Добавить точку с запятой после @B = NULLIF (@B, ''), если вы хотите, чтобы предложение WITH использовалось в вашем заявлении. – Quassnoi

ответ

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 

Чтобы получить первые 10 строки, используйте:

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 

Для извлечения строк между 11 и 20, используйте:

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

Просто любопытно, для чего именно СО? Улучшает ли производительность? – Xaisoft

+0

В этом случае было проще копировать и вставлять с помощью: :) Это полезно для CTE, другими способами оно ведет себя так же, как и подзапрос. – Quassnoi

+0

Я собираюсь обновить свою процедуру в своем вопросе, потому что это на самом деле дало мне ошибку, говоря, что мне нужно прекратить мое утверждение, так что, возможно, вы сможете обновить свой ответ, когда я обновляю свой вопрос. Спасибо, пока. – 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 

Это должно сделать трюк. У меня нет SSMS передо мной, чтобы проверить, но вы МОЖЕТЕ подменять использование «cnt» в заказе ROW_NUMBER по предложению со вторым вызовом функции, но это должно дать вам общую идею.

+0

Это дает мне ошибку: Неправильный синтаксис рядом с ключевым словом 'over' – Xaisoft

+0

К сожалению, должно быть ROW_NUMBER() –

+0

Я пробовал это, и он дал мне ту же ошибку. – Xaisoft

3

Я хотел бы использовать подзапрос для получения значений функции в результат, а затем функцию ранжирования ROW_NUMBER, например:

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 

Если вы хотите только определенную часть результатов (например, для подкачки), то вам нужно будет еще один вложенный запрос, а затем фильтровать по номеру строки:

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 

Обратите внимание, что в этом запросе, вы могли бы поместите ROW_NUMBER в любом месте в списке выбора, так как вы больше не зависимы от использования синтаксиса «order by 1» для инструкции order by.

При вызове этого запроса существует несколько тонких проблем. Не гарантируется, что порядок, в котором возвращаются записи, будет согласован, если количество элементов в каждой группе не является уникальным. Чтобы решить эту проблему, вам необходимо изменить функцию ROW_NUMBER, чтобы упорядочить поля, которые составляют группу в счете.

В этом случае, было бы А и В, в результате чего:

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 

Это заканчивается заказом результаты последовательно между вызовами когда подсчет элементов между группами не является уникальным (при условии, один и тот же набор данных).

+0

почему заказ на 1? – Xaisoft

+0

До сих пор ваша работа сработала, но мне все еще интересно узнать порядок на 1, и если я захочу вернуть строки с 1 по 10, где бы я разместил предложение where? – Xaisoft

+0

@Xiasoft: заказать по первому полю. Возможно, что у вас будут одинаковые подсчеты. Чтобы обойти это, вы должны поместить больше полей заказа в раздел (по заказу ...). Это повлияет на row_number, который является единственным, на что вы должны заказывать (отсюда и 1). – casperOne

Смежные вопросы