18

我有一個頁面,我有4個標籤顯示基於不同表格的4個不同的報告。數(*)真的很貴嗎?

我使用select count(*) from <table>查詢獲得每個表的行數,並顯示選項卡上每個表中可用的行數。因此,每次頁面回發都會導致執行5個count(*)查詢(4個獲取計數和1個分頁)以及1個獲取報告內容的查詢。

現在我的問題是:count(*)查詢真的很貴 - 我應該在頁面的視圖狀態中保持行計數(至少是那些顯示在標籤上的),而不是多次查詢?

COUNT(*)查詢的代價是多少?

+0

您正在使用哪個數據庫? (和哪個版本?) – penguat 2010-04-27 10:36:49

+0

sql server 2005 – 2010-04-27 10:40:47

+1

在實際負載下你的頁面加載速度是否太慢?如果不是,那麼計數不是「太貴」,所以你不應該擔心。 – 2010-04-27 10:40:57

回答

7

您需要附加SQL Profileran app level profiler like L2SProf看看之前在你的背景下,真正的查詢費用:

  • 猜測的問題是什麼,並試圖確定一個潛在的解決方案可能帶來的益處

  • 允許別人在da interwebs上爲你猜測 - 有很多沒有引用的錯誤信息,包括在這個帖子中(但不是在這篇文章中:P)

當你這樣做,這將是明確的,最好的方法是什麼 - 即SELECT COUNT是否支配的東西與否,等等

,並具有做到這一點,您就可以知道是否您選擇做的任何更改都會產生積極或消極的影響。

+0

感謝您的接受!謹慎地簡單告訴我們你最終決定做什麼和/或在途中發現? – 2010-04-28 07:53:12

8

一般而言,COUNT(*)成本的成本與滿足查詢條件的記錄數加上準備這些記錄所需的時間(取決於底層查詢複雜度)成正比。

在處理單個表格的簡單情況下,經常會有特定的優化措施來降低此類操作的成本。例如,從MySQL中的單個MyISAM表中執行COUNT(*)而沒有WHERE條件 - 這是瞬時的,因爲它存儲在元數據中。

例如,讓我們考慮兩個查詢:

SELECT COUNT(*) 
FROM largeTableA a 

由於每個記錄滿足查詢時,COUNT(*)成本是成正比的表中的記錄數(即,正比於它返回什麼)(假設它需要訪問的行和到位心不是一個具體的優化處理它)

SELECT COUNT(*) 
FROM largeTableA a 
JOIN largeTableB b 
ON  a.id = b.id 

在這種情況下,發動機將最有可能使用HASH JOIN和執行計劃將是這樣的:

  1. 建立在表
  2. 的小哈希表掃描較大的表,在哈希表
  3. 查找每個記錄數的比賽,因爲他們去。

在這種情況下,COUNT(*)開銷(步驟3)可以忽略不計以及查詢時間通過將步驟1和2被完全定義,正在建設的哈希表和尋找它。對於這樣的查詢,時間將是O(a + b):它並不真正依賴於匹配的數量。

但是,如果有兩個a.idb.id索引,MERGE JOIN可以被選擇和COUNT(*)時間將正比於匹配的數量再次,由於一個索引查找每個匹配之後將被執行。

+0

當然是指「COUNT(\ *)成本與數量或記錄成正比,加上聯繫數據庫所需的時間」。這並不意味着COUNT(*)操作無法與記錄總數成正比,是嗎? – Armand 2010-04-27 10:55:47

+0

@Alison:查看帖子更新。 – Quassnoi 2010-04-27 11:19:39

+0

我的評論現在沒有多大意義 - 這是對先前評論的回覆。 – Armand 2010-04-27 13:27:06

0

COUNT(*)可能會特別昂貴,因爲它可能會導致加載(和分頁)整個表,其中可能只需要對主鍵進行計數(在某些實現中它已被優化)。

從它的聲音來看,您每次都會導致表加載操作,這很慢,但除非運行明顯緩慢或導致某種問題,否則不要優化:過早和不必要的優化可能會導致很大的麻煩!

一個索引主鍵的計數會快得多,但有了索引的成本可能沒有任何好處。

+2

這不適用於SQL Server(T-SQL) - count(*)已經過優化,並且是計算每一行的首選方法。 – 2010-04-27 10:43:45

+0

-1您需要在提供此類權限之前提供參考,並且沒有附帶條件 – 2010-04-27 10:47:01

+3

參考:http://thehobt.blogspot.com/2008/12/debunking-myth-select-count-vs-select.html – 2010-04-27 10:58:26

0

所有的I/O都很貴,如果你沒有它就可以完成任務,你應該。但如果需要的話,我不會擔心。

您提到將計數存儲在視圖狀態中,當然這是一個選項,只要該代碼的行爲在該計數錯誤時可接受,因爲基礎記錄已消失或已添加到該計數中。

2

正如其他人所說的COUNT(*)總是物理計數行,所以如果你可以做一次並緩存結果,那肯定是可取的。

如果您進行基準測試並確定成本可以忽略不計,則您(當前)沒有問題。

如果事實證明,通過使用

SELECT rows FROM sysindexes WHERE id = OBJECT_ID('sometable') AND indid < 2

將返回是,你可以讓你的分頁「模糊」,如「顯示1至大約爲30,000 500」太貴了,您的方案一個近似值的行數(其近似值,因爲它沒有更新,直到CHECKPOINT)。

+0

爲了澄清,這種技術只測量表中的近似行 - 它不會在存在where子句或連接的情況下工作(除了瑣碎的笛卡爾連接),對嗎? – 2012-08-08 19:24:55

+0

是的,它僅適用於簡單的快照 – 2012-08-09 09:39:41

0

這取決於您對此表中的數據做什麼。如果他們經常改變,並且每次都需要他們,也許你可以做出觸發器來填充另一個只包含來自該表格的計數的表格。如果你需要單獨顯示這些數據,也許你可以只爲一個特定的表執行「select count(*)...」。我立即想到了這一點,但我敢肯定,還有其他方法可以加快這一進程。緩存數據,也許? :)

1

如果頁面變慢,您可以查看的一件事是儘量減少數據庫往返次數。即使你的COUNT(*)查詢是O(1),如果你做得足夠多,這肯定會減慢速度。

不是一次設置和執行5個單獨的查詢,而是在單個批處理中運行SELECT語句,並一次處理5個結果。

也就是說,如果你使用ADO.NET,這樣做(檢查略去了錯誤;無環/非動態爲清楚起見):

string sql = "SELECT COUNT(*) FROM Table1; SELECT COUNT(*) FROM Table2;" 

SqlCommand cmd = new SqlCommand(sql, connection); 
SqlDataReader dr = cmd.ExecuteReader(); 

// Defaults to first result set 
dr.Read(); 
int table1Count = (int)dr[0]; 

// Move to second result set 
dr.NextResult(); 
dr.Read(); 
int table2Count = (int)dr[0]; 

如果您使用的是有些類型的ORM,比如NHibernate,應該有一種方法來啓用自動查詢批處理。