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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM