SQL Server 2012でのクエリの結果として、ExtJSでレポートを作成します。
ページングでExt.Gridを使用しているため、オフセットを使用して、クエリが返すレコードの量を制限できます。ただし、このExtJS機能が機能するためには、指定したクエリが持つレコードの合計量を提供する必要があります。
一部のレポートは、日中にフィルターとデータ変更を使用します。これは、クエリを実行することによってのみ合計数を知ることができるためです。その結果、2回実行されます。1回はcount(*)で合計数を取得し、もう1回は適切なデータを取得します。
現在実行しているように2回実行する必要がなく、すべてのレコードをループすることなく、offsetコマンドがある場合でも、クエリの合計数を見つける方法はありますか?
残念ながら、クエリが完了するまで、レコードの総数はわかりません。これは、何とかして無制限のクエリを終了させ、その後行のサブセットを取得する必要があることを意味します。
これを行う1つの方法は、COUNT(*) OVER () AS [TotalRows]
をSELECTリストに追加することです。これは、すべての行に対して同じ値が繰り返される列として値を返しますが、それでも変わりません。
_SELECT [object_id], [schema_id], [name], [type_desc], COUNT(*) OVER () AS [TotalRows]
FROM sys.objects
ORDER BY [object_id] ASC OFFSET 10 ROWS FETCH NEXT 7 ROWS ONLY;
_
もちろん、それは自由に利用できる値にアクセスするだけではありません。上記のクエリの実行プランを、COUNT(*)..
式なしの同じもののプランと比較するだけです。
_SELECT [object_id], [schema_id], [name], [type_desc]
FROM sys.objects
ORDER BY [object_id] ASC OFFSET 10 ROWS FETCH NEXT 7 ROWS ONLY;
_
私が思いついた別の方法は、Stack Overflowでほぼ同じ質問に対する私の回答で詳細に説明されています。
TSQL:返される行を制限し、制限なしで(すべての行に追加せずに)返される合計数をカウントする方法はありますか?
その質問とこの質問の違いは、他の質問は、カウントしている結果セットの列として値を具体的に返さないことです。また、もう1つの質問は「制限」の側面に関するものであり、この質問は両方ではないにしても少なくとも「オフセット」に関するものです。しかし、「オフセット」の部分を追加することは難しくありません。次のようなことを行うには、目的の結果を返すループの前にループを追加するだけです。
_// assume an input param or variable of: int Offset;
int _RowCounter = 0;
while (Reader.Read() && ++_RowCounter < Offset);
_
おもしろいことに、クエリを実行して行を取得せずに行数を取得できる別のメカニズムを試してみると思いました:CURSOR(はい、EVILCURSOR)。変数_@@CURSOR_ROWS
_は、行をOPEN
したことがない場合でも、FETCH
を呼び出した後に入力されます。
以下では(どれだけうまく機能するかはわかりませんが)最初の行をフェッチするために循環する必要がないので、_FETCH ABSOLUTE x
_を使用して "オフセット"アスペクトを処理します。ただし、ABSOLUTE
を使用すると、_FAST_FORWARD
_や_FORWARD_ONLY
_などのオプションを使用できなくなります。 _FAST_FORWARD
_を使用できないため、次にSTATIC
を使用しましたが、STATIC
の追加が実際に役立つか害を及ぼすかどうかをテストする必要があります。
今、望ましい結果を得る唯一の方法は、それらをテーブル変数に格納することです(または一時テーブルは機能しますが、この場合はテーブル変数の方が良いと思います)。これにより、単一の結果セットとして返すことができます。 FETCH
句なしでINTO
を実行すると、呼び出しごとに異なる結果セットが返されます(おそらく望ましくない)。
_DECLARE @Offset INT = 10,
@Limit INT = 7;
DECLARE cursed CURSOR LOCAL READ_ONLY STATIC -- can't use FAST_FORWARD or FORWARD_ONLY :(
FOR SELECT so.[object_id], so.[schema_id], so.name, so.type_desc
FROM sys.objects so
ORDER BY so.[object_id] ASC;
DECLARE @object_id INT,
@schema_id INT,
@name sysname,
@type_desc NVARCHAR(60);
DECLARE @RowCounter INT = 0,
@StartRow INT = (@Offset + 1); -- Offset is how many rows to skip
DECLARE @Results TABLE ([object_id] INT NOT NULL, [schema_id] INT NOT NULL,
[name] sysname NOT NULL, [type_desc] NVARCHAR(60) NOT NULL);
OPEN cursed; -- execute the query
SELECT @@CURSOR_ROWS AS [RowCountBeforeRetrievingAnyRows];
FETCH ABSOLUTE @StartRow -- position cursor at beginning of desired range
FROM cursed
INTO @object_id, @schema_id, @name, @type_desc;
WHILE (@@FETCH_STATUS = 0 AND @RowCounter < @Limit)
BEGIN
INSERT INTO @Results ([object_id], [schema_id], [name], [type_desc])
VALUES (@object_id, @schema_id, @name, @type_desc);
SET @RowCounter += 1;
FETCH NEXT
FROM cursed
INTO @object_id, @schema_id, @name, @type_desc;
END;
CLOSE cursed;
DEALLOCATE cursed;
-- return desired range of rows
SELECT * FROM @Results;
-----------------------------------------------
-- check the actual data to see if it worked
SELECT [object_id], [schema_id], [name], [type_desc], COUNT(*) OVER () AS [TotalRows]
FROM sys.objects
ORDER BY [object_id] ASC OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY;
_
_STATIC CURSOR
_が結果をtempdbに保存することを考えると、以下は基本的に操作上は同じですが、少し直接的です。
_DECLARE @Offset INT = 10,
@Limit INT = 7;
DECLARE @Results TABLE ([object_id] INT NOT NULL, [schema_id] INT NOT NULL,
[name] sysname NOT NULL, [type_desc] NVARCHAR(60) NOT NULL,
[RowID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY);
INSERT INTO @Results ([object_id], [schema_id], [name], [type_desc])
SELECT so.[object_id], so.[schema_id], so.name, so.type_desc
FROM sys.objects so
ORDER BY so.[object_id] ASC;
SELECT @@ROWCOUNT AS [TotalRows];
-- return desired range via OFFSET / FETCH (Clustered Index Scan)
SELECT [object_id], [schema_id], [name], [type_desc]
FROM @Results
ORDER BY [RowID] ASC OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY;
-- return desired range via WHERE (Clustered Index Seek)
SELECT [object_id], [schema_id], [name], [type_desc]
FROM @Results
WHERE [RowID] > @Offset
AND [RowID] <= (@Offset + @Limit)
ORDER BY [RowID] ASC;
-----------------------------------------------
-- check the actual data to see if it worked. OR, maybe this is actually better?
SELECT [object_id], [schema_id], [name], [type_desc], COUNT(*) OVER () AS [TotalRows]
FROM sys.objects
ORDER BY [object_id] ASC OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY;
_