2011-02-26 24 views
2

我試圖做分頁在SQL Server存儲過程,就像這樣:保留合計結果信息雖然分頁在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)

回答

3

您可以使用窗口集合函數。即下面的例子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 
+0

我忘記了窗口集合,+1。 – 2011-02-26 18:11:39

+0

嗯...好主意。所以,用我的方法,我可以將全部結果放在一個變量中,但我必須使用臨時表。使用你的方法,你可以使用CTE,但是你必須調用Count()來獲得總結果,而不是僅僅獲取@@ RowCount中已有的結果。而且你必須返回結果集中每一行的Count字段......我真的不確定從性能的角度來看這是否更好。 – Tedderz 2011-02-26 18:24:17

+1

@Tedderz - 從性能POV我不確定哪個最好。使用窗口化聚合函數爲計劃添加了一個假脫機,並且往往會增加很多邏輯讀取次數。如果有一個避免假脫機的計劃會更好,但到目前爲止我不認爲這是可能的。 – 2011-02-26 18:36:35

3

據我所知,這是不可能既要返回行和與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 
+0

+1這確實意味着2種排序操作,但可能有更好的執行計劃,我的答案。 – 2011-02-26 20:21:48

+0

@Martin你太謙虛 - 你的代碼應該有一個更好的計劃,即使這是一個時髦。兩者都需要一個單獨的窗口,但這需要對每一行進行編號並執行棘手的數學運算。 +1兩者 – RichardTheKiwi 2011-02-26 21:51:27