您的位置:首页 > 运维架构

如何提高性能SELECT TOP n * FROM [tablename] ORDER BY NEWID()

2012-04-21 10:47 429 查看
如何提高性能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的子查询,排序的内容少
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: