MySQL: Cách nhanh nhất để đếm số hàng


117

Cách nào để đếm một số hàng sẽ nhanh hơn trong MySQL?

Điều này:

SELECT COUNT(*) FROM ... WHERE ...

Hoặc, thay thế:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

Người ta sẽ nghĩ rằng phương pháp đầu tiên sẽ nhanh hơn, vì đây rõ ràng là lãnh thổ cơ sở dữ liệu và công cụ cơ sở dữ liệu phải nhanh hơn bất kỳ ai khác khi xác định những thứ như thế này trong nội bộ.


1
Ồ, tôi đã tìm thấy một câu hỏi tương tự ( stackoverflow.com/questions/1855226/… ). Nhưng sau đó, tôi sử dụng SELECT 1và không SELECT *. Có sự khác biệt?
Franz

tôi không biết, nhưng có thể hình dung được rằng hai câu trả lời này giống hệt nhau - trình tối ưu hóa truy vấn mysql có thể làm điều tương tự trên mỗi câu trả lời. điều đó nói rằng cái trước ít mơ hồ hơn cái sau. tại sao bạn không viết một số điểm chuẩn và kiểm tra nó?
Jesse Cohen

Uhm, chúng ta hãy giả sử tôi đang cố gắng để nâng cao tầm nhìn của công cụ tìm kiếm SO bằng cách hỏi một câu hỏi tương tự trong từ khác nhau;)
Franz

1
Sự khác biệt là lượng dữ liệu được gửi sang phía PHP. Bạn càng có nhiều cột, thì SELECT * càng chậm hơn so với SELECT 1, vì tất cả các cột được truy xuất thay vì chỉ số 1. mysql_query()Ví dụ: khi bạn chạy , toàn bộ tập kết quả được gửi đến PHP từ MySQL, bất kể bạn là gì. làm với dữ liệu đó.
toon81

Đặt một câu hỏi như thế này là một cách tuyệt vời để có được cái nhìn sâu sắc hoặc ý tưởng mới, nhưng cuối cùng nếu bạn thực sự có một tình huống cụ thể mà bạn muốn tăng tốc độ hơn, bạn sẽ phải chạy thử nghiệm để xem đâu là nhanh nhất.
still_dreaming_1

Câu trả lời:


124

Khi bạn COUNT(*)tính chỉ số cột đếm, vì vậy nó sẽ là kết quả tốt nhất. Mysql với công cụ MyISAM thực sự lưu trữ số hàng, nó không đếm tất cả các hàng mỗi khi bạn cố gắng đếm tất cả các hàng. (dựa trên cột của khóa chính)

Sử dụng PHP để đếm hàng không thông minh lắm, vì bạn phải gửi dữ liệu từ mysql sang php. Tại sao phải làm điều đó khi bạn có thể đạt được điều tương tự ở phía mysql?

Nếu COUNT(*)chậm, bạn nên chạy EXPLAINtrên truy vấn và kiểm tra xem các chỉ mục có thực sự được sử dụng hay không và chúng nên được thêm vào đâu.


Cách sau không phải là cách nhanh nhất , nhưng có một trường hợp, nơi COUNT(*)không thực sự phù hợp - khi bạn bắt đầu nhóm kết quả, bạn có thể gặp vấn đề, nơi COUNTkhông thực sự đếm tất cả các hàng.

Giải pháp là SQL_CALC_FOUND_ROWS. Điều này thường được sử dụng khi bạn đang chọn hàng nhưng vẫn cần biết tổng số hàng (ví dụ: đối với phân trang). Khi bạn chọn các hàng dữ liệu, chỉ cần nối SQL_CALC_FOUND_ROWStừ khóa sau CHỌN:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

Sau khi bạn đã chọn các hàng cần thiết, bạn có thể nhận được số lượng với một truy vấn duy nhất này:

SELECT FOUND_ROWS();

FOUND_ROWS() phải được gọi ngay sau truy vấn chọn dữ liệu.


Tóm lại, mọi thứ thực sự phụ thuộc vào số lượng mục nhập bạn có và những gì có trong câu lệnh WHERE. Bạn thực sự nên chú ý đến cách các chỉ mục đang được sử dụng, khi có rất nhiều hàng (hàng chục nghìn, hàng triệu và nhiều hơn).


14
Sửa lại: MyISAMlưu trữ số hàng. Các công cụ lưu trữ khác như InnoDB không lưu trữ số hàng và sẽ đếm tất cả các hàng mỗi lần .
The Scrum Meister

1
Bạn có biết cách nào sẽ nhanh nhất khi bạn chỉ muốn tìm xem có hàng: SELECT 1 FROM ... LIMIT 1hoặc SELECT COUNT(*) FROM ...không?
Franz

1
Có thể hữu ích khi lưu ý rằng nếu bạn vẫn cần dữ liệu và chỉ muốn số lượng cho phân trang / v.v. sẽ hiệu quả hơn khi lấy dữ liệu sau đó đếm các hàng trong chương trình của bạn.
Tyzoid

6
Không liên quan đến việc động cơ lưu trữ hàng. Câu hỏi nêu rõ có một WHEREmệnh đề.
Álvaro González

1
@Franz SELECT COUNT(*) FROM ...có thể mất thời gian đáng kể, tùy thuộc vào những gì phải được quét (ví dụ: một bảng rất lớn hoặc chỉ mục hàng triệu / tỷ / nghìn tỷ hàng). SELECT 1 FROM ... LIMIT 1trả về ngay lập tức vì bạn đang giới hạn nó ở hàng đầu tiên.
jbo5112

59

Sau khi nói chuyện với các đồng đội của tôi, Ricardo nói với chúng tôi rằng cách nhanh hơn là:

show table status like '<TABLE NAME>' \G

Nhưng bạn phải nhớ rằng kết quả có thể không chính xác.

Bạn cũng có thể sử dụng nó từ dòng lệnh:

$ mysqlshow --status <DATABASE> <TABLE NAME>

Thông tin thêm: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

Và bạn có thể tìm thấy một cuộc thảo luận đầy đủ tại mysqlperformanceblog


2
Đối với InnoDB, đây là một con số gần đúng.
Martin Tournoij

2
Đây là điều tuyệt vời để biết khi cần ý tưởng sơ bộ về số hàng trong bảng rất lớn, nơi đếm (*) có thể mất hàng giờ!
Mark Hansen

Điều này đã giúp tôi không bị nhổ hết lông. COUNT (*) đang tính tuổi để đếm tất cả 33 triệu hàng cộng trong cơ sở dữ liệu của tôi. Dù sao, tôi chỉ muốn biết liệu chức năng xóa các hàng song song của tôi có hoạt động hay không. Tôi không cần một con số chính xác.
joemar.ct

1
+1 Sử dụng trạng thái bảng thay thế "COUNT (*)" sẽ là câu trả lời chính xác cho câu hỏi này vì là về "nhanh nhất" chứ không phải "độ chính xác".
lepe

2
Việc sử dụng SHOW TABLE STATUS(hoặc tương đương SELECTtrong information_schema) rất nhanh, nhưng nó không xử lý một WHEREmệnh đề. Nó chính xác đối với MyISAM, nhưng không chính xác (đôi khi bị lệch bởi hệ số 2) đối với InnoDB.
Rick James

29

Câu hỏi hay, câu trả lời hay. Đây là một cách nhanh chóng để lặp lại kết quả nếu ai đó đang đọc trang này và bỏ sót phần đó:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");

5
mysql_query là một hàm không được dùng nữa kể từ PHP 5.5.0.
Omar Tariq

8
Tại sao không as count? idlà khó hiểu trong cái nhìn đầu tiên.
Orkhan Alikhanov

Không trả lời câu hỏi
mindic

17

Truy vấn này (tương tự như những gì bayuah đã đăng ) hiển thị một bản tóm tắt đẹp về số lượng bảng bên trong cơ sở dữ liệu: (phiên bản đơn giản của thủ tục được lưu trữ bởi Ivan Cachicatari mà tôi rất khuyên dùng).

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

Thí dụ:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |

Nó cho tôi một kết quả. Nhưng kết quả từ số đếm (1) và số này khác nhau. Cách này luôn cho một số ít hơn truy vấn đếm. Có suy nghĩ gì không?
Ayyappan Sekar

3
Chỉ là một lưu ý cho độc giả. Phương pháp này cực kỳ nhanh nhưng nó chỉ được áp dụng khi bạn có thể làm việc với số hàng gần đúng vì giá trị được lưu trữ trong information_schemakhông giống với giá trị được trả về SELECT count(*) FROMtrong trường hợp sử dụng InnoDB. Nếu bạn cần giá trị nghiêm ngặt thì hãy nhớ rằng phương pháp này chỉ cung cấp giá trị nghiêm ngặt với các bảng MyISAM. Với InnoDB số hàng là gần đúng.
Bartosz Firyn

13

Tôi luôn hiểu rằng những điều dưới đây sẽ cho tôi thời gian phản hồi nhanh nhất.

SELECT COUNT(1) FROM ... WHERE ...

1
CHỌN 1 TỪ ... ĐÂU ... sẽ không nhanh hơn?
Patrick

3
@patrick - SELECT 1 ...sẽ trở lại như nhiều hàng như WHERELIMITyêu cầu, và tất cả họ sẽ là "1".
Rick James

1
show table status like '<TABLE NAME>' Điều này sẽ nhanh hơn nhiều.
sâu

@deep - nhưng không liên quan nếu bạn có một WHEREđiều khoản. Và, đối với InnoDB, nó chỉ là ước tính.
Rick James

@RickJames vâng đúng!
sâu

6

Nếu bạn cần đếm toàn bộ tập kết quả, bạn có thể thực hiện phương pháp sau:

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

Điều này thường không nhanh hơn so với việc sử dụng COUNTmặc dù người ta có thể nghĩ ngược lại vì nó thực hiện tính toán nội bộ và không gửi dữ liệu trở lại người dùng, do đó, việc cải thiện hiệu suất bị nghi ngờ.

Thực hiện hai truy vấn này tốt cho việc phân trang để lấy tổng nhưng không đặc biệt để sử dụng WHEREmệnh đề.


Giao nhau. Điều đó có hoạt động trên các hệ thống cơ sở dữ liệu phổ biến nhất không? MySQL, Postgres, SQLite ...?
Franz

4
Điều này thực sự thường không nhanh hơn sử dụng COUNT (*) chút nào. Xem stackoverflow.com/questions/186588/…
toon81

2
Bạn nên RẤT cẩn thận khi sử dụng chức năng này. Việc sử dụng thiếu thận trọng của nó đã từng khiến toàn bộ môi trường sản xuất của chúng tôi bị đình trệ. Nó là rất nhiều tài nguyên, vì vậy hãy sử dụng cẩn thận.
Janis Peisenieks 13/1213

6

Tôi đã thực hiện một số điểm chuẩn để so sánh thời gian thực thi của COUNT(*)vs COUNT(id)(id là khóa chính của bảng - được lập chỉ mục).

Số lượng thử nghiệm: 10 * 1000 truy vấn

Kết quả: COUNT(*)nhanh hơn 7%

XEM ĐỒ THỊ: đồ thị điểm chuẩn

Lời khuyên của tôi là sử dụng: SELECT COUNT(*) FROM table


1
Tôi cũng có một cách phổ biến để đếm COUNT(1), sẽ rất thú vị khi xem một số điểm chuẩn ở đó ...
Sliq

4

Thử cái này:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";

@lepe Tôi xin lỗi. Ý tôi là, thật tuyệt nếu ai đó không ủng hộ đưa ra một số lời giải thích tại sao anh ấy / cô ấy làm như vậy, để mọi người có thể học được điều gì đó về nó.
bayuah

1
Điều này sẽ cung cấp cho bạn một câu trả lời gần đúng một cách nhanh chóng. Nếu bạn cần một câu trả lời chính xác, bạn cần phải thực hiện select count(*) from table_namehoặc một cái gì đó khác. dba.stackexchange.com/questions/151769/…
Programster

@Programster Cảm ơn bạn. Tốt hơn là để tôi trong bóng tối gần một năm.
bayuah

1
@bayuah Tôi không chắc ý của bạn về nhận xét cuối cùng của bạn. Tôi chỉ có thể cho rằng bạn nghĩ rằng tôi là người đã bỏ phiếu thấp cho câu trả lời của bạn, mà tôi thì không.
trình

1
@Programster Không, tôi xin lỗi, tôi không có ý đó. Ý tôi là cảm ơn vì lời giải thích của bạn, vì vậy tôi có thể phỏng đoán những gì có thể Downvoter nghĩ khi anh ấy / cô ấy làm điều đó.
bayuah

3

Có lẽ bạn có thể muốn xem xét làm một SELECT max(Id) - min(Id) + 1. Điều này sẽ chỉ hoạt động nếu Id của bạn liên tục và các hàng không bị xóa. Tuy nhiên nó rất nhanh.


3
Hãy cẩn thận: máy chủ đôi khi sử dụng giá trị tăng tự động> 1 (vì lý do sao lưu), vì vậy giải pháp này là tốt nhưng bạn nên kiểm tra cấu hình DB của mình trước.
Alex,

1

EXPLAIN SELECT id FROM ....đã làm thủ thuật cho tôi. và tôi có thể thấy số hàng trong rowscột của kết quả.


0

Tôi đã xử lý các bảng cho Chính phủ Đức với đôi khi 60 triệu bản ghi.

Và chúng tôi cần biết nhiều lần tổng số hàng.

Vì vậy, chúng tôi lập trình cơ sở dữ liệu quyết định rằng trong mỗi bảng là một bản ghi luôn là bản ghi trong đó tổng số bản ghi được lưu trữ. Chúng tôi đã cập nhật số này, tùy thuộc vào các hàng CHÈN hoặc XÓA.

Chúng tôi đã thử tất cả các cách khác. Đây là cách nhanh nhất.


1
và chi tiết về cách bạn cập nhật hàng đó là gì? Điều đó có nghĩa là mặc dù một thiết kế bị lỗi cho một bảng, trong đó tất cả các hàng sẽ yêu cầu một số nguyên lãng phí đi cùng cho chuyến đi.
Drew

5
Yea, thật là ngu ngốc haha. Với mọi truy vấn, bạn phải bỏ qua hàng đầu tiên. Tôi sẽ chỉ tạo một bảng tổng và điền bảng đó dựa trên một trình kích hoạt. Bảng người dùng trên chèn, cập nhật bảng tổng số. Bảng người dùng trên xóa, cập nhật bảng tổng số.
HTMLGuy

-1

Một câu lệnh count (*) với điều kiện trong đó trên khóa chính đã trả lại số lượng hàng nhanh hơn nhiều giúp tôi tránh bị quét toàn bộ bảng.

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

Điều này nhanh hơn nhiều đối với tôi

SELECT COUNT(*) FROM ...
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.