web-dev-qa-db-ja.com

Microsoft SQL Server 2000でMySQLのLIMIT句をエミュレートします

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()関数を使用できる場合、適切なソリューションが存在します。

39
Bill Karwin

次に、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
5
SELECT TOP n *
FROM tablename
WHERE key NOT IN (
    SELECT TOP x key
    FROM tablename
    ORDER BY key
    DESC
);
5
a0p

LIMITのみが必要な場合、ms sqlには同等のTOPキーワードがあるため、明らかです。 OFFSETでLIMITが必要な場合、前述のようないくつかのハックを試すことができますが、それらはすべてオーバーヘッドを追加します。これらのカスケードはすべて必要ではないと思います。私の意見で最もクリーンなソリューションは、SQL側でオフセットなしでTOPを使用し、phpのmssql_data_seekなどの適切なクライアントメソッドで必要な開始レコードを検索することですこれは純粋なSQLソリューションではありませんが、オーバーヘッドを追加しないため、最良のソリューションだと思います(スキップしたレコードは、過去にシークしたときにネットワーク上で転送されません。 )。

4
grr

ORMでこれを実装しようとしますが、かなり簡単です。 SQL Serverに本当に必要な場合は、次のlinq to sqlステートメントについてlinq to sqlによって生成されたコードを見て、そこから進みます。そのコードを実装したMSFTエンジニアは、長年SQLチームの一員であり、彼が何をしていたかを知っていました。

var result = myDataContext.mytable.Skip(pageIndex * pageSize).Take(pageSize)

0
Barka