如何提高性能SELECT TOP n * FROM [tablename] ORDER BY NEWID()
想从数据库随机获得一条记录
在网上查了一下全都是这个方法
但性能太差 千万级的表要20秒以上
如何提高性能
SELECT TOP 1 * FROM [tablename] ORDER BY NEWID()
SELECT * FROM [tablename]
where id=(select count(*)+1 from tablename)*rand()
如果id连续,这个就是最快的方法。
SELECT top 1 * FROM [tablename]
where id>=(select max(*) from tablename)*rand()
ID不连续~~
试试这样能否接受:
SQL code
SELECT TOP 10 * FROM tb tablesample (10000 Rows)
e...
效率好像还好,只是08才支持。
还有另外一个方法是在表加一个计算列,值就是NEWID(),
SELECT 的时候 计算列 LIKE RAND()生成的几个字母排列,这个一定程度上也可以达到随机的目的,而且不用扫描整个表
综合上面的两种方法实测结果如下,这里temp表的rest是主键,temp表里有20000行记录:
方法1——平均耗时22秒:
SQL code
DECLARE @i INT ;
DECLARE @r INT;
SET @i=1;
WHILE @i<2000
BEGIN
SET @r=(SELECT TOP 1 rest FrOM temp ORDER BY NEWID());
SET @i=@i+1;
END
方法2——平均耗时24秒:
SQL code
DECLARE @i INT ;
DECLARE @r INT;
SET @i=1;
WHILE @i<2000
BEGIN
SET @r=(SELECT rest FROM temp WHERE rest IN (SELECT TOP 1 rest FrOM temp ORDER BY NEWID()));
SET @i=@i+1;
END
方法3,即一开始说的方式,平均耗时10秒:
SQL code
DECLARE @i INT ;
DECLARE @r INT;
SET @i=1;
WHILE @i<2000
BEGIN
SET @r=(SELECT TOP 1 rest FROM temp WHERE rest NOT IN
(SELECT TOP(
SELECT CAST((SELECT COUNT(*) FROM temp)*RAND() AS INT)
) temp.rest FROM temp));
SET @i=@i+1;
END
可见该方式应该是最快的了。仅针对有主键(或者索引)的表。
Use the following method:
SELECT * FROM [tablename] where pk in (select top 1 pk from [tablename] ORDER BY NEWID())
PK is the primary key - hopefully with a clustered index. If it is the case, th……
虽然pk是聚集索引,但newid()是要对每一行生成一个列值,那么肯是要进行clustered index scan.
所以
SELECT * FROM [tablename] where pk in (select top 1 pk from [tablename] ORDER BY NEWID())
应该是会比
select top 10 * from [tablename] ORDER BY NEWID()
更耗IO。
但是如果再给pk建一个非聚集索引就不一样了。
create index ix_pk on [tablename](pk)
这样的话,仅仅是一个包含pk值的非聚集索引的page数要比整个clustered index page要少的多。
所以,要用这种写法来提升性能,需要再给pk建一个非聚集索引。
6楼的方法正解,而且这也是对于此类问题最常见的用法。
方法1
SELECT * FROM [tablename] where pk in (select top 1 pk from [tablename] ORDER BY NEWID())
方法2
SELECT top 1 * FROM [tablename] ORDER BY NEWID()
这两个方法貌似效率都差不多,其实不然。方法1中的子查询给了查询优化器更多的选择。
这里认为PK是聚集的,如果这个表上有其他的非聚集索引呢?
可以肯定的是方法1一定比方法2效率高,子查询的处理完全可以在非聚集索引上完成。
如果这个表只有主键,没有其他任何非聚集索引,方法1的查询等同于:
方法3
SELECT * FROM [tablename] where pk in (select top 1 pk from [tablename] WITH (INDEX(1)) ORDER BY NEWID())
方法3还是会比方法2效率高一点,首先明确一点方法3和方法2涉及到的IO基本是一致的。
不同就在于两个查询对CPU消耗量的差异,方法2相比方法3在进行排序取TOP1的操作上,
消耗的cpu要比方法3要多一些,原因在于除了聚集索引扫描之外的操作,方法2是都是基于整行记录进行的,
而方法3是只要基于主键字段。
楼主的要求:想从数据库随机获得一条记录
条件:千万级的表
数据库:暂时理解为SQL SERVER 2005+
解决方案:
select top 1 * from tbname TABLESAMPLE(XXX rows)
XXX应该为整数,算法如下:
按照你表的行大小,计算一个数据页大概装多少行,XXX最少为每页的行数,建议填为2-3倍每页行数
如果不愿意去算这个数,也可以写成如下形式:
select top 1 * from tbname TABLESAMPLE(0.1 PERCENT)
由于TABLESAMPLE选项只是读取整页的数据,因此即使是很大很大的表,读取几个页的速度还是很快的
使用TABLESAMPLE选项,数据库随机取一个或者几个数据页,然后返回结果,不扫描索引,也不做全表扫描,也不排序。
这个时候不要被执行计划所误导,执行计划写的是Table Scan.
这个时候SET STATISTICS IO ON,你可以看到只有几个逻辑和物理读取
分析下上面几个方法:
1.SELECT * FROM [tablename] where pk in (select top 1 pk from [tablename] ORDER BY NEWID())
2.SELECT top 1 * FROM [tablename] ORDER BY NEWID()
这两个方法都需要排序,因此IO的逻辑读取会比较多,而且CPU占用也多很多
方法1比方法2好,因为方法1的子查询,排序的内容少