2008-10-19 28 views
37

Khi tôi làm việc trên Zend Framework's database component, chúng tôi đã cố gắng trừu tượng hóa chức năng của mệnh đề LIMIT được hỗ trợ bởi MySQL, PostgreSQL và SQLite. Đó là, tạo ra một truy vấn có thể được thực hiện theo cách này:Giả lập mệnh đề LIMIT của MySQL trong Microsoft SQL Server 2000

$select = $db->select(); 
$select->from('mytable'); 
$select->order('somecolumn'); 
$select->limit(10, 20); 

Khi cơ sở dữ liệu hỗ trợ LIMIT, điều này tạo ra một truy vấn SQL như sau:

SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20 

này được phức tạp hơn cho các thương hiệu của cơ sở dữ liệu không hỗ trợ LIMIT (mệnh đề đó không phải là một phần của ngôn ngữ SQL chuẩn, nhân tiện). Nếu bạn có thể tạo số hàng, hãy đặt toàn bộ truy vấn một bảng dẫn xuất và truy vấn bên ngoài sử dụng BETWEEN. Đây là giải pháp cho Oracle và IBM DB2. Microsoft SQL Server 2005 có một chức năng hàng-số tương tự, vì vậy người ta có thể viết các truy vấn theo cách này:

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]; 

Tuy nhiên, Microsoft SQL Server 2000 không có ROW_NUMBER() chức năng.

Vì vậy, câu hỏi của tôi là, bạn có thể đưa ra một cách để mô phỏng chức năng LIMIT trong Microsoft SQL Server 2000, chỉ sử dụng SQL? Không sử dụng con trỏ hoặc T-SQL hoặc thủ tục lưu sẵn. Nó phải hỗ trợ cả hai đối số cho LIMIT, cả đếm và bù. Các giải pháp sử dụng bảng tạm thời cũng không được chấp nhận.

Edit:

Các giải pháp phổ biến nhất cho MS SQL Server 2000 có vẻ là như hình dưới đây, ví dụ để có được hàng 50 đến 75:

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

Tuy nhiên, điều này doesn' t làm việc nếu tổng số kết quả là, nói 60 hàng. Truy vấn bên trong trả về 60 hàng vì nó nằm trong 75 đầu. Sau đó truy vấn bên ngoài trả về các hàng 35-60, không phù hợp với "trang" mong muốn của 50-75. Về cơ bản, giải pháp này hoạt động trừ khi bạn cần "trang" cuối cùng của tập hợp kết quả không xảy ra là bội số của kích thước trang.

Edit:

Một giải pháp khác hoạt động tốt hơn, nhưng chỉ khi bạn có thể giả định tập hợp kết quả bao gồm một cột mà là duy nhất:

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

Kết luận:

Không chung giải pháp đa năng dường như tồn tại để mô phỏng LIMIT trong MS SQL Server 2000. Một giải pháp tốt tồn tại nếu bạn có thể sử dụng ROW_NUMBER() funct ion trong MS SQL Server 2005.

Trả lời

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

Có, đây là gần, nhưng nó chỉ hoạt động khi có một chìa khóa duy nhất trong kết quả truy vấn tạm thời. Làm thế nào bạn sẽ làm điều đó trong một truy vấn GROUP BY hoặc cho một truy vấn tham gia nhiều bảng? –

4

Khi bạn chỉ cần LIMIT, ms sql có từ khóa TOP tương đương, vì vậy rõ ràng. Khi bạn cần LIMIT với OFFSET, bạn có thể thử một số hacks như đã mô tả trước đây, nhưng tất cả đều thêm một số phí, tức là đặt hàng một chiều và cách khác, hoặc hoạt động NOT IN mở rộng. Tôi nghĩ rằng tất cả những thác đó là không cần thiết. Giải pháp sạch nhất trong ý kiến ​​của tôi sẽ chỉ sử dụng TOP mà không có bù đắp ở phía SQL, và sau đó tìm kiếm bản ghi bắt đầu bắt buộc bằng phương thức máy khách thích hợp, như mssql_data_seek trong php.Mặc dù đây không phải là giải pháp SQL thuần túy, nhưng tôi nghĩ đó là giải pháp tốt nhất vì nó không thêm bất kỳ chi phí nào (các bản ghi bị bỏ qua sẽ không được chuyển trên mạng khi bạn tìm kiếm chúng, nếu đó là những gì bạn lo lắng).

+0

Tôi đồng ý với đề xuất của bạn khi thiết kế một ứng dụng. Trong trường hợp của tôi, tôi đã thiết kế một khung truy cập cơ sở dữ liệu được cho là có một API nhất quán nhưng tạo ra các SQL khác nhau khi cần thiết cho các nhãn hiệu cơ sở dữ liệu khác nhau. Các giải pháp đã được trong việc chuẩn bị SQL, không phải trong lấy. –

+1

Ý tưởng hay, cảm ơn bạn rất nhiều. Nói chung tôi nghĩ rằng giải pháp của bạn là khả thi hơn. – 0plus1

+0

Đó là lý do tại sao tôi không thích MSSQL! –

5

Đây là một giải pháp khác chỉ hoạt động trong Sql Server 2005 và mới hơn vì nó sử dụng câu lệnh ngoại trừ. Nhưng tôi vẫn chia sẻ nó. Nếu bạn muốn nhận được các hồ sơ 50-75 viết:

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

Cảm ơn, có vẻ như nó sẽ làm việc quá trong Microsoft SQL Server 2005. Nó vẫn còn lại cho chúng tôi không có giải pháp tốt cho Microsoft SQL Server 2000. Nhưng tôi đoán kể từ năm 2009, nó cuối cùng hợp lý để thả hỗ trợ cho năm 2000 anyway. –

+0

Có, nó hoạt động trong Sql Server 2005. –

+0

Đó là thông minh! Tôi đã không nghĩ đến việc làm theo cách đó. – jocull

0

tôi sẽ cố gắng để thực hiện điều này trong ORM của tôi vì nó là khá đơn giản ở đó. Nếu nó thực sự cần phải được trong SQL Server sau đó tôi sẽ xem xét mã được tạo ra bởi LINQ để sql cho linq sau đây để sql tuyên bố và đi từ đó. Các kỹ sư MSFT người thực hiện mã đó là một phần của đội ngũ SQL trong nhiều năm và biết những gì ông đã làm.

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

+0

http://msdn.microsoft.com/en-us/library/bb386988.aspx nói: "Bản dịch là khác nhau cho SQL Server 2000 và SQL Server 2005. Nếu bạn định sử dụng Bỏ qua (Của TSource) với truy vấn của bất kỳ phức tạp, sử dụng SQL Server 2005. " –

+0

Điểm của tôi là có rất nhiều giải pháp tốt cho vấn đề này nếu điều kiện tiên quyết là chạy MS SQL Server 2005. Nhưng vài trong số chúng hoạt động trong trường hợp chung trên MS SQL Server 2000. Phải thừa nhận rằng, bây giờ là năm 2011 và ngày càng khó khăn để biện minh cho chạy MS SQL Server 2000 như thời gian tuần hành trên. Tuy nhiên, một số người vẫn làm! :( –

Các vấn đề liên quan