Zend Frameworkのデータベースコンポーネント に取り組んだとき、MySQL、PostgreSQL、およびSQLiteでサポートされているLIMIT
句の機能を抽象化しようとしました。つまり、クエリの作成は次の方法で行うことができます。
_$select = $db->select();
$select->from('mytable');
$select->order('somecolumn');
$select->limit(10, 20);
_
データベースがLIMIT
をサポートしている場合、次のようなSQLクエリが生成されます。
_SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20
_
これは、LIMIT
をサポートしないデータベースのブランドではより複雑でした(ちなみに、この句は標準SQL言語の一部ではありません)。行番号を生成できる場合は、クエリ全体を派生テーブルにし、外部クエリでBETWEEN
を使用します。これは、OracleおよびIBM DB2のソリューションでした。 Microsoft SQL Server 2005には同様の行番号関数があるため、次のようにクエリを記述できます。
_SELECT z2.*
FROM (
SELECT ROW_NUMBER OVER(ORDER BY id) AS zend_db_rownum, z1.*
FROM ( ...original SQL query... ) z1
) z2
WHERE z2.zend_db_rownum BETWEEN @offset+1 AND @offset+@count;
_
ただし、Microsoft SQL Server 2000にはROW_NUMBER()
関数がありません。
私の質問は、SQLのみを使用して、Microsoft SQL Server 2000のLIMIT
機能をエミュレートする方法を思いつくことができますか?カーソル、T-SQL、またはストアドプロシージャを使用しません。 LIMIT
の両方の引数、カウントとオフセットの両方をサポートする必要があります。一時テーブルを使用するソリューションも受け入れられません。
編集:
MS SQL Server 2000の最も一般的なソリューションは、たとえば行50〜75を取得するなど、次のようなもののようです。
_SELECT TOP 25 *
FROM (
SELECT TOP 75 *
FROM table
ORDER BY BY field ASC
) a
ORDER BY field DESC;
_
ただし、合計結果セットがたとえば60行の場合、これは機能しません。内側のクエリは上位75行にあるため60行を返します。その後、外側のクエリは行35〜60を返しますが、これは目的の50〜75の「ページ」に収まりません。基本的に、このソリューションは、ページサイズの倍数である結果セットの最後の「ページ」が必要でない限り機能します。
編集:
別の解決策はより効果的ですが、結果セットに一意の列が含まれていると想定できる場合のみです。
_SELECT TOP n *
FROM tablename
WHERE key NOT IN (
SELECT TOP x key
FROM tablename
ORDER BY key
);
_
結論:
MS SQL Server 2000でLIMIT
をエミュレートするための汎用ソリューションは存在しないようです。MSSQL Server 2005でROW_NUMBER()
関数を使用できる場合、適切なソリューションが存在します。
次に、SQL Server 2005以降でのみ動作する別のソリューションを示します。これは、exceptステートメントを使用しているためです。とにかく共有します。レコード50から75を取得したい場合:
select * from (
SELECT top 75 COL1, COL2
FROM MYTABLE order by COL3
) as foo
except
select * from (
SELECT top 50 COL1, COL2
FROM MYTABLE order by COL3
) as bar
SELECT TOP n *
FROM tablename
WHERE key NOT IN (
SELECT TOP x key
FROM tablename
ORDER BY key
DESC
);
LIMITのみが必要な場合、ms sqlには同等のTOPキーワードがあるため、明らかです。 OFFSETでLIMITが必要な場合、前述のようないくつかのハックを試すことができますが、それらはすべてオーバーヘッドを追加します。これらのカスケードはすべて必要ではないと思います。私の意見で最もクリーンなソリューションは、SQL側でオフセットなしでTOPを使用し、phpのmssql_data_seekなどの適切なクライアントメソッドで必要な開始レコードを検索することですこれは純粋なSQLソリューションではありませんが、オーバーヘッドを追加しないため、最良のソリューションだと思います(スキップしたレコードは、過去にシークしたときにネットワーク上で転送されません。 )。
ORMでこれを実装しようとしますが、かなり簡単です。 SQL Serverに本当に必要な場合は、次のlinq to sqlステートメントについてlinq to sqlによって生成されたコードを見て、そこから進みます。そのコードを実装したMSFTエンジニアは、長年SQLチームの一員であり、彼が何をしていたかを知っていました。
var result = myDataContext.mytable.Skip(pageIndex * pageSize).Take(pageSize)