2008-10-19 81 views
37

當我處理Zend Framework's database component時,我們試圖抽象出MySQL,PostgreSQL和SQLite支持的LIMIT子句的功能。也就是說,創建一個查詢可以做到這樣:在Microsoft SQL Server 2000中模擬MySQL 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的解決方案。微軟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 @[email protected]; 

但是,Microsoft SQL Server 2000不具備ROW_NUMBER()功能。

所以我的問題是,你能想出一種方法來模仿Microsoft SQL Server 2000中的LIMIT功能,僅使用SQL?不使用遊標或T-SQL或存儲過程。它必須支持LIMIT這兩個參數,包括計數和偏移量。使用臨時表的解決方案也是不可接受的。

編輯:

的MS SQL Server 2000中最常見的解決方案似乎是類似下面,例如通過75獲得行50:

SELECT TOP 25 * 
FROM ( 
    SELECT TOP 75 * 
    FROM table 
    ORDER BY BY field ASC 
) a 
ORDER BY field DESC; 

然而,這並未」如果總的結果集是60行,則工作。內部查詢返回60行,因爲它位於前75位。然後,外部查詢返回行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。如果您可以使用ROW_NUMBER()函數,存在一個很好的解決方案離子在MS SQL Server 2005.

回答

4
SELECT TOP n * 
FROM tablename 
WHERE key NOT IN (
    SELECT TOP x key 
    FROM tablename 
    ORDER BY key 
    DESC 
); 
+0

是的,這是接近的,但它只有在用戶在臨時查詢結果的唯一關鍵工作。你如何在GROUP BY查詢或連接多個表的查詢中執行此操作? – 2009-04-06 17:55:41

4

當您只需要LIMIT時,ms sql具有等效的TOP關鍵字,因此很清楚。 當你需要帶OFFSET的LIMIT時,你可以嘗試一些像上面描述的那樣的黑客攻擊,但是它們都會增加一些開銷,即用於一種方式然後另一種方式,或者是非開銷操作。 我認爲所有這些瀑布是不需要的。在我的oppinion最乾淨的解決方案將只是使用TOP沒有在SQL端的偏移量,然後尋找所需的開始記錄與適當的客戶端方法,如在php中的mssql_data_seek。雖然這不是一個純粹的SQL解決方案,但我認爲它是最好的解決方案,因爲它不會增加任何開銷(當您搜索它們時,跳過的記錄不會在網絡上傳輸,如果這是您的擔憂)。

+0

我同意您在設計應用程序時的建議。就我而言,我設計了一個數據庫訪問框架,該框架應該具有一致的API,但根據不同品牌的數據庫的需要生成不同的SQL。解決方案必須在SQL準備中,而不是在獲取中。 – 2009-05-13 15:30:46

+1

不錯的主意,非常感謝。總的來說,我認爲你的解決方案更可行。 – 0plus1 2010-04-26 09:57:10

+0

這就是我不喜歡MSSQL的原因! – 2011-09-05 05:23:42

5

這是另一種解決方案,它只適用於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 
0

我會嘗試在我的ORM實現這個,因爲它是非常簡單的存在。如果它確實需要在SQL Server中,那麼我會查看由linq to sql生成的以下linq to sql語句的代碼並從那裏開始。實施該代碼的MSFT工程師多年來一直是SQL團隊的一員,並且知道他在做什麼。

VAR的結果= myDataContext.mytable.Skip(PageIndex的* pageSize的)。取(pageSize的)