Answers:
我会避免使用SELECT *
。指定您实际上想要的列,即使可能是全部。
SQL Server 2005以上版本
SELECT col1, col2
FROM (
SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow
SQL Server 2000
SELECT *
意味着如果表的结构发生变化,您的查询仍会运行,但会给出不同的结果。如果添加了列,这可能会很有用(尽管您仍然必须在某处使用它的名称);如果删除或重命名了列,那么对SQL而言,明显可见的中断要比对代码进行进一步的更改更好,因为变量未初始化。
如果您要处理,以便所有页面,然后简单地记住看到前面的页面上的最后一个关键值,并使用TOP (25) ... WHERE Key > @last_key ORDER BY Key
可以得到最好的执行方法,如果存在合适的索引,让这有效地seeked -或API游标,如果他们不这样做。
对于选择arbitary页面的最佳解决方案为SQL Server 2005 - 2008 R2可能是ROW_NUMBER
和BETWEEN
对于SQL Server 2012+,可以根据需要使用增强的ORDER BY子句。
SELECT *
FROM MyTable
ORDER BY OrderingColumn ASC
OFFSET 50 ROWS
FETCH NEXT 25 ROWS ONLY
这是一种方法(SQL2000)
SELECT * FROM
(
SELECT TOP (@pageSize) * FROM
(
SELECT TOP (@pageNumber * @pageSize) *
FROM tableName
ORDER BY columnName ASC
) AS t1
ORDER BY columnName DESC
) AS t2
ORDER BY columnName ASC
这是另一种方式(SQL 2005)
;WITH results AS (
SELECT
rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
, *
FROM tableName
)
SELECT *
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize
您可以使用ROW_NUMBER()
function获得所需的内容:
SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
有OFFSET .. FETCH
在SQL Server 2012中,但你需要指定一个ORDER BY
列。
如果您确实没有可以作为ORDER BY
列传递的任何显式列(如其他人所建议),则可以使用以下技巧:
SELECT * FROM MyTable
ORDER BY @@VERSION
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY
... 要么
SELECT * FROM MyTable
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY
当用户未明确指定订单时,我们在jOOQ中使用它。然后,这将产生相当随机的排序,而无需任何额外费用。
对于具有更多大数据列的表,我更喜欢:
SELECT
tablename.col1,
tablename.col2,
tablename.col3,
...
FROM
(
(
SELECT
col1
FROM
(
SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
FROM tablename
WHERE ([CONDITION])
)
AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
)
AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);
--
[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.
它在具有大数据的表(如BLOB)上具有更好的性能,因为ROW_NUMBER函数只需要浏览一列,并且只有匹配的行与所有列一起返回。
您可能无法直接执行此操作,具体取决于您的版本,但是您可以做一些像
select top 25 *
from (
select top 75 *
from table
order by field asc
) a
order by field desc
其中“字段”是关键。
使用该ROW_NUMBER() OVER (ORDER BY)
语句时应小心,因为性能相当差。使用通用表表达式的情况也一样ROW_NUMBER()
,甚至更糟。我正在使用以下代码段,事实证明该代码段比使用带有标识的表变量提供页码的速度要快。
DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10
DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT
SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1
SELECT * FROM
(
SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
(
SELECT *, 1 As SortConst FROM #ResultSet
) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT
DROP TABLE #ResultSet
我使用此技术进行分页。我没有获取所有行。例如,如果我的页面需要显示前100行,则仅获取100个带有where子句的行。SQL的输出应具有唯一键。
该表具有以下内容:
ID, KeyId, Rank
同一排名将分配给多个KeyId。
SQL是 select top 2 * from Table1 where Rank >= @Rank and ID > @Id
第一次我都通过了0。第二次通过1和14。第三次通过2和6 ....
第10条记录Rank&Id的值将传递到下一个
11 21 1
14 22 1
7 11 1
6 19 2
12 31 2
13 18 2
这对系统的压力最小
在SqlServer2005中,您可以执行以下操作:
DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10
SELECT
*
FROM
(
SELECT
row_number()
OVER
(ORDER BY column) AS rownum, column2, column3, .... columnX
FROM
table
) AS A
WHERE
A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1)
@Offset + @Limit - 1
吗?如果@Limit为10,则将返回11行。
做到这一点而又不浪费时间订购记录的最佳方法是这样的:
select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY
不到一秒钟!
大表的最佳解决方案。
我已经搜索了一段时间(针对通用查询),并找到了另一种在SQL Server 2000+上使用ROWCOUNT和游标且没有TOP或任何临时表的方式。
使用SET ROWCOUNT [OFFSET+LIMIT]
可以限制结果,并使用游标直接转到所需的行,然后循环到最后。
因此,您的查询将如下所示:
SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0
使用SQL Server 2012(11.x)和更高版本以及Azure SQL数据库,您还可以具有“ fetch_row_count_expression”,还可以具有ORDER BY子句。
USE AdventureWorks2012;
GO
-- Specifying variables for OFFSET and FETCH values
DECLARE @skip int = 0 , @take int = 8;
SELECT DepartmentID, Name, GroupName
FROM HumanResources.Department
ORDER BY DepartmentID ASC
OFFSET @skip ROWS
FETCH NEXT @take ROWS ONLY;
注意 OFFSET指定在开始从查询表达式返回行之前要跳过的行数。它不是起始行号。因此,必须为0才能包含第一条记录。