Как использовать ROW_NUMBER в следующей процедуре?
У меня есть следующая хранимая процедура, которая возвращает A
, B
и счетчик в порядке убывания. Я пытаюсь использовать ROW_NUMBER
, поэтому я могу записать записи, но я хочу, чтобы номер первой строки 1
был записью с наивысшим счетчиком, поэтому в основном, если я верну таблицу с 3 записями, а счет 30
, 20
, 10
, то номер строки 1
должен соответствовать счету 30
, номер строки 2
должен соответствовать счету 20
, а номер строки 3
должен соответствовать счету 10
. dbo.f_GetCount
- это функция, возвращающая счетчик.
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;
Ответы
Ответ 1
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
Ответ 2
Я бы использовал подзапрос, чтобы получить значения функции в результате, а затем функцию ранжирования 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 в любом месте в списке выбора, так как вы больше не зависимы от использования синтаксиса "порядок по 1" для инструкции order by.
Здесь возникает тонкая проблема при вызове этого запроса несколько раз. Не гарантируется, что порядок, в котором возвращаются записи, будет согласован, если количество элементов в каждой группе не является уникальным. Чтобы решить эту проблему, вам нужно изменить функцию ROW_NUMBER, чтобы упорядочить по полям, составляющим группу в счете.
В этом случае это будут A и B, в результате чего:
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
Это приводит к постоянному упорядочиванию результатов между вызовами, когда количество элементов между группами не уникально (при условии того же набора данных).
Ответ 3
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 order со вторым вызовом функции, но это должно дать вам общую идею.