Сохранение общей информации о результатах при разбивке на страницы в SQL Server - PullRequest
2 голосов
/ 26 февраля 2011

Я пытаюсь сделать нумерацию страниц в хранимой процедуре в SQL Server, например:

/*Assign a row number to each row*/
SELECT
ROW_NUMBER() OVER (ORDER BY A, B, C ASC) AS ROW_NUMBER,
A, B, C
FROM ABC
WHERE ROW_NUMBER BETWEEN @startRecordNumber and @endRecordNumber

Но мой код вызова также хочет знать, сколько результатов было в исходном наборе результатов до нумерации страниц. Итак, я изменяю свой код на это:

/*Put the results into a temp table first*/
SELECT
ROW_NUMBER() OVER (ORDER BY A, B, C ASC) AS ROW_NUMBER,
A, B, C
INTO #TOTAL_RESULTS
FROM ABC

/*Get the total results from @@RowCount*/
declare @totalResults bigint
set @totalResults = @@RowCount

/*Now just get the desired page from the temp table*/
SELECT
A, B, C
FROM #TOTAL_RESULTS
WHERE ROW_NUMBER BETWEEN @startRecordNumber and @endRecordNumber

DROP TABLE #TOTAL_RESULTS

Мне это кажется довольно окольным. Есть ли способ получить размер исходного результирующего набора без необходимости создавать временную таблицу? Может быть, просто выражение общей таблицы вместо? Кажется, я не могу понять, как это сделать.

Если это имеет значение, вот схема ABC:

ABC
A (PK, smallint not null)
B (PK, smallint not null)
C (PK, smallint not null)

Ответы [ 2 ]

3 голосов
/ 26 февраля 2011

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

Но если это нормально, возвращать сумму в виде столбца, и еслив столбцах A, B и C нет дубликатов в таблице, вы можете вернуть итоговое значение, например, так:

SELECT
  A, B, C,
  TotalResults = RowNumAsc + RowNumDesc - 1
FROM (
  SELECT
    A, B, C,
    RowNumAsc = ROW_NUMBER() OVER (ORDER BY A, B, C),
    RowNumDesc = ROW_NUMBER() OVER (ORDER BY A DESC, B DESC, C DESC)
  FROM
) s
WHERE RowNumAsc BETWEEN @startRecordNumber AND @endRecordNumber
3 голосов
/ 26 февраля 2011

Вы можете использовать оконные агрегатные функции.то есть COUNT(*) OVER() пример ниже.

;WITH cte As
(
SELECT *, 
        ROW_NUMBER() OVER (ORDER BY number) AS RN,
        COUNT(*) OVER() AS Cnt
FROM master..spt_values
)
SELECT *
FROM cte 
WHERE RN BETWEEN 101 and 200

Или- Не очень серьезное предложение, но оно избегает катушек, рабочих столов и двойной сортировки: -)

DECLARE @Spid INT = @@Spid
DECLARE @TraceID INT

DECLARE @maxfilesize BIGINT = 5
DECLARE @filepath NVARCHAR(200) = N'C:\trace_' + LEFT(NEWID(),36)

EXEC sp_trace_create @TraceID OUTPUT, 0, @filepath, @maxfilesize, NULL 

exec sp_trace_setevent @TraceID, 146, 1, 1
exec sp_trace_setevent @TraceID, 146, 22, 1
exec sp_trace_setevent @TraceID, 146, 34, 1
exec sp_trace_setevent @TraceID, 146, 51, 1
exec sp_trace_setevent @TraceID, 146, 12, 1
-- filter for spid
EXEC sp_trace_setfilter @TraceID, 12, 0, 0, @Spid
-- start the trace
EXEC sp_trace_setstatus @TraceID, 1


;WITH cte AS
(
SELECT number, type, name, 
        ROW_NUMBER() OVER (ORDER BY number, type, name) AS RN
FROM master..spt_values
)
SELECT * FROM cte
WHERE RN BETWEEN 101 AND 200
OR RN+1 =0 /*To stop a "TOP 200" getting added to the plan*/

;WITH  XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as sql)
     SELECT  ActualRows
         FROM   fn_trace_getinfo(@TraceID) fn
                CROSS APPLY fn_trace_gettable(CAST(value AS NVARCHAR(200)), 1)
                CROSS APPLY (SELECT CAST(TextData AS XML) AS xPlan) x
                CROSS APPLY (SELECT T.relop.value('@ActualRows', 'INT') AS ActualRows
                             FROM   xPlan.nodes('//sql:RelOp[@LogicalOp="Segment"]/sql:RunTimeInformation/sql:RunTimeCountersPerThread') T(relop)) ca
         WHERE  property = 2
                AND ObjectName<>'fn_trace_getinfo' AND TextData NOT LIKE '%ThisQuery%'  

-- Stop the trace
EXEC sp_trace_setstatus @TraceID, 0
-- Close and delete the trace
EXEC sp_trace_setstatus @TraceID, 2
...