zoukankan      html  css  js  c++  java
  • 如何提高性能SELECT TOP n * FROM [tablename] ORDER BY NEWID()

    如何提高性能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的子查询,排序的内容少

  • 相关阅读:
    [公告]Google个性化主页可以正常阅读博客园的RSS了
    致歉
    [公告]网站程序已经升级到ASP.NET 2.0
    GTF: Great Teacher Friedman
    Node.js : exports と module.exports の違い
    拨开历史的迷雾从篡夺者战争到五王之战的政经原因
    javascript模板系统 ejs v10
    window.name + postMessage实现不用代理页的跨域通信
    node.js Domain 時代のエラー処理のコーディングパターン
    鲜为人知的get,set操作符
  • 原文地址:https://www.cnblogs.com/qanholas/p/2461088.html
Copyright © 2011-2022 走看看