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