MySQL: วิธีที่เร็วที่สุดในการนับจำนวนแถว


117

วิธีใดในการนับจำนวนแถวที่ควรเร็วกว่าใน MySQL

นี้:

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

หรือทางเลือกอื่น:

SELECT 1 FROM ... WHERE ...

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

ใครจะคิดว่าวิธีแรกน่าจะเร็วกว่าเนื่องจากเป็นอาณาเขตของฐานข้อมูลอย่างชัดเจนและเครื่องมือฐานข้อมูลควรเร็วกว่าวิธีอื่นเมื่อกำหนดสิ่งต่างๆเช่นนี้ภายใน


1
โอ้ฉันพบคำถามที่คล้ายกัน ( stackoverflow.com/questions/1855226/… ) แต่แล้วผมใช้และไม่ได้SELECT 1 SELECT *มีความแตกต่างหรือไม่?
Franz

ฉันไม่รู้ แต่เป็นไปได้ว่าคำตอบทั้งสองนี้เหมือนกัน - เครื่องมือเพิ่มประสิทธิภาพการสืบค้น mysql อาจทำสิ่งเดียวกันในแต่ละข้อ ที่กล่าวว่าอดีตมีความคลุมเครือน้อยกว่าอย่างหลัง ทำไมคุณไม่เขียนเกณฑ์มาตรฐานและทดสอบดูล่ะ
Jesse Cohen

อืมสมมติว่าฉันพยายามเพิ่มการมองเห็นเครื่องมือค้นหาของ SO โดยถามคำถามที่คล้ายกันในคำอื่น ๆ ;)
ฟรานซ์

1
ความแตกต่างคือจำนวนข้อมูลที่ส่งไปยังฝั่ง PHP ยิ่งคุณมีคอลัมน์มากเท่าใด SELECT * ก็ยิ่งช้าลงเมื่อเทียบกับ SELECT 1 เนื่องจากคอลัมน์ทั้งหมดจะถูกดึงออกมาแทนที่จะเป็นเพียงตัวเลข 1 mysql_query()เท่านั้นตัวอย่างเช่นเมื่อคุณเรียกใช้ชุดผลลัพธ์ทั้งหมดจะถูกส่งไปยัง PHP จาก MySQL โดยไม่คำนึงถึงสิ่งที่คุณ ทำกับข้อมูลนั้น
toon81

การถามคำถามเช่นนี้เป็นวิธีที่ดีในการได้รับข้อมูลเชิงลึกหรือแนวคิดใหม่ ๆ แต่ในที่สุดหากคุณมีสถานการณ์เฉพาะที่คุณต้องการความเร็วมากขึ้นคุณจะต้องทำการทดสอบเพื่อดูว่าอะไรเร็วที่สุด
still_dreaming_1

คำตอบ:


125

เมื่อคุณCOUNT(*)ใช้ดัชนีคอลัมน์การนับดังนั้นจึงเป็นผลลัพธ์ที่ดีที่สุด Mysql ที่มีเอ็นจิ้นMyISAMจะเก็บการนับแถวมันจะไม่นับแถวทั้งหมดในแต่ละครั้งที่คุณพยายามนับแถวทั้งหมด (ตามคอลัมน์ของคีย์หลัก)

การใช้ PHP เพื่อนับแถวนั้นไม่ค่อยฉลาดนักเพราะคุณต้องส่งข้อมูลจาก mysql ไปยัง php ทำไมในเมื่อคุณสามารถบรรลุสิ่งเดียวกันในฝั่ง mysql?

ถ้าCOUNT(*)ช้าคุณควรเรียกใช้EXPLAINแบบสอบถามและตรวจสอบว่ามีการใช้ดัชนีจริงหรือไม่และควรเพิ่มที่ใด


ต่อไปนี้ไม่ใช่วิธีที่เร็วที่สุดแต่มีบางกรณีที่COUNT(*)ไม่เหมาะสมจริงๆ - เมื่อคุณเริ่มจัดกลุ่มผลลัพธ์คุณอาจพบปัญหาโดยที่COUNTไม่ได้นับแถวทั้งหมดจริงๆ

วิธีแก้คือSQL_CALC_FOUND_ROWS. โดยปกติจะใช้เมื่อคุณเลือกแถว แต่ยังจำเป็นต้องทราบจำนวนแถวทั้งหมด (เช่นสำหรับการเพจ) เมื่อคุณเลือกแถวข้อมูลเพียงแค่ต่อท้ายSQL_CALC_FOUND_ROWSคำสำคัญหลัง SELECT:

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

หลังจากที่คุณเลือกแถวที่ต้องการแล้วคุณจะได้รับการนับด้วยแบบสอบถามเดียวนี้:

SELECT FOUND_ROWS();

FOUND_ROWS() จะต้องถูกเรียกทันทีหลังจากแบบสอบถามเลือกข้อมูล


สรุปแล้วทุกอย่างขึ้นอยู่กับจำนวนรายการที่คุณมีและสิ่งที่อยู่ในคำสั่ง WHERE คุณควรให้ความสนใจกับการใช้ดัชนีเมื่อมีแถวจำนวนมาก (หลายหมื่นหลายล้านขึ้นไป)


14
การแก้ไข: จัดMyISAMเก็บจำนวนแถว เครื่องมือการเก็บรักษาอื่น ๆ เช่นInnoDB ไม่เก็บนับแถวและจะนับแถวทั้งหมดในแต่ละครั้ง
The Scrum Meister

1
คุณรู้หรือไม่ว่าสิ่งใดจะเร็วที่สุดเมื่อคุณต้องการทราบว่ามีแถว: SELECT 1 FROM ... LIMIT 1หรือSELECT COUNT(*) FROM ...?
Franz

1
อาจเป็นประโยชน์ที่จะทราบว่าหากคุณต้องการข้อมูลอยู่แล้วและต้องการเพียงการนับเลขหน้า / etc จะมีประสิทธิภาพมากขึ้นในการรับข้อมูลจากนั้นนับแถวในโปรแกรมของคุณ
Tyzoid

6
ไม่เกี่ยวข้องว่าเครื่องยนต์จะจัดเก็บการนับแถวหรือไม่ คำถามระบุชัดเจนว่ามีWHEREประโยค
ÁlvaroGonzález

1
@Franz SELECT COUNT(*) FROM ...อาจใช้เวลาพอสมควรขึ้นอยู่กับสิ่งที่ต้องสแกน (เช่นตารางขนาดใหญ่มากหรือดัชนีเป็นล้าน / พันล้าน / ล้านล้านแถว) SELECT 1 FROM ... LIMIT 1ส่งคืนทันทีเนื่องจากคุณกำลัง จำกัด ไว้ที่แถวแรก
jbo5112

59

หลังจากพูดคุยกับเพื่อนร่วมทีมริคาร์โดบอกเราว่าวิธีที่เร็วกว่าคือ:

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

แต่คุณต้องจำไว้ว่าผลลัพธ์อาจไม่แน่นอน

คุณสามารถใช้งานได้จากบรรทัดคำสั่งเช่นกัน:

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

ข้อมูลเพิ่มเติม: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

และคุณสามารถค้นหาการอภิปรายทั้งหมดได้ที่mysqlperformanceblog


2
สำหรับ InnoDB นี่เป็นค่าประมาณ
Martin Tournoij

2
นี่เป็นเรื่องดีที่จะทราบเมื่อต้องการทราบคร่าวๆเกี่ยวกับจำนวนแถวในตารางขนาดใหญ่มากซึ่ง count (*) อาจใช้เวลาหลายชั่วโมง!
Mark Hansen

สิ่งนี้ช่วยฉันจากการดึงเส้นขนทั้งหมดออก COUNT (*) ใช้เวลาในการนับทั้งหมด 33 ล้านแถวบวกในฐานข้อมูลของฉัน อย่างไรก็ตามฉันแค่อยากรู้ว่าฟังก์ชันลบแถวขนานของฉันใช้งานได้หรือไม่ ฉันไม่ต้องการตัวเลขที่แน่นอน
joemar.ct

1
+1 การใช้สถานะตารางแทน "COUNT (*)" ควรเป็นคำตอบที่ถูกต้องสำหรับคำถามนี้เช่นเดียวกับ "เร็วที่สุด" ไม่ใช่ "ความถูกต้อง"
lepe

2
การใช้SHOW TABLE STATUS(หรือเทียบเท่าSELECTในinformation_schema) นั้นรวดเร็ว แต่ไม่ได้จัดการกับWHEREประโยคคำสั่ง เป็นข้อมูลที่แม่นยำสำหรับ MyISAM แต่ไม่ชัดเจน (บางครั้งอาจมีค่าเท่ากับ 2) สำหรับ InnoDB
Rick James

29

คำถามที่ดีคำตอบที่ดี นี่เป็นวิธีที่รวดเร็วในการสะท้อนผลลัพธ์หากใครก็ตามที่อ่านหน้านี้และพลาดส่วนนั้นไป:

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

5
mysql_query เป็นฟังก์ชันที่เลิกใช้แล้วเมื่อ PHP 5.5.0
Omar Tariq

8
ทำไมไม่as count? idสับสนในการมองครั้งแรก
Orkhan Alikhanov

ไม่ตอบคำถาม
Mentalic

17

แบบสอบถามนี้ (ซึ่งคล้ายกับสิ่งที่ Bayuah โพสต์ ) แสดงข้อมูลสรุปที่ดีของตารางทั้งหมดที่นับภายในฐานข้อมูล: ( ขั้นตอนการจัดเก็บเวอร์ชันที่เรียบง่ายโดย Ivan Cachicatariซึ่งฉันขอแนะนำเป็นอย่างยิ่ง)

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'; 

ตัวอย่าง:

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

มันทำให้ฉันได้ผลลัพธ์ แต่ผลลัพธ์จาก count (1) และอันนี้ต่างกัน วิธีนี้จะให้จำนวนน้อยกว่าการสืบค้นแบบ count เสมอ ความคิดใด ๆ ?
Ayyappan Sekar

3
เพียงแค่แจ้งให้ผู้อ่านทราบ วิธีนี้เร็วมาก แต่ใช้ได้ก็ต่อเมื่อคุณสามารถทำงานกับจำนวนแถวโดยประมาณเนื่องจากค่าที่เก็บไว้information_schemaไม่เหมือนกับค่าที่ส่งคืนSELECT count(*) FROMในกรณีที่ใช้ InnoDB หากคุณต้องการค่าที่เข้มงวดโปรดทราบว่าวิธีนี้ให้ค่าที่เข้มงวดกับตาราง MyISAM เท่านั้น ด้วย InnoDB จำนวนแถวเป็นค่าประมาณคร่าวๆ
Bartosz Firyn

13

ฉันเข้าใจมาตลอดว่าด้านล่างนี้จะทำให้ฉันตอบสนองได้เร็วที่สุด

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

1
จะไม่เลือก 1 จาก ... ที่ไหน ... เร็วกว่านี้?
patrick

3
@patrick - SELECT 1 ...จะคืนค่าแถวให้มากที่สุดเท่าที่WHEREและLIMITขอและทั้งหมดจะเป็น "1"
Rick James

1
show table status like '<TABLE NAME>' นี้จะเร็วขึ้นมาก
ลึก

@deep - แต่ไม่เกี่ยวข้องหากคุณมีWHEREอนุประโยค และสำหรับ InnoDB เป็นเพียงการประมาณเท่านั้น
Rick James

@RickJames ใช่จริง!
ลึก

6

หากคุณต้องการนับชุดผลลัพธ์ทั้งหมดคุณสามารถใช้แนวทางต่อไปนี้:

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

โดยปกติจะไม่เร็วกว่าการใช้งานCOUNTแม้ว่าใคร ๆ จะคิดว่าในทางตรงกันข้ามเป็นเช่นนั้นเนื่องจากกำลังทำการคำนวณภายในและไม่ได้ส่งข้อมูลกลับไปยังผู้ใช้ดังนั้นจึงสงสัยว่าจะมีการปรับปรุงประสิทธิภาพ

การทำแบบสอบถามทั้งสองนี้เหมาะสำหรับการแบ่งหน้าสำหรับการรับผลรวม แต่ไม่ใช่โดยเฉพาะอย่างยิ่งสำหรับการใช้WHEREอนุประโยค


intersting ใช้งานได้กับระบบฐานข้อมูลทั่วไปหรือไม่ MySQL, Postgres, SQLite ... ?
Franz

4
ซึ่งมักจะไม่เร็วกว่าการใช้ COUNT (*) เลย ดูstackoverflow.com/questions/186588/…
toon81

2
คุณควรระมัดระวังอย่างมากเมื่อใช้ฟังก์ชันนี้ การใช้งานโดยประมาทครั้งหนึ่งทำให้สภาพแวดล้อมการผลิตทั้งหมดของเราหยุดชะงักลง เป็นทรัพยากรที่เข้มข้นมากดังนั้นควรใช้ด้วยความระมัดระวัง
Janis Peisenieks

6

ฉันทำการวัดประสิทธิภาพเพื่อเปรียบเทียบเวลาดำเนินการของCOUNT(*)vs COUNT(id)(id คือคีย์หลักของตาราง - จัดทำดัชนี)

จำนวนการทดลอง: 10 * 1,000 แบบสอบถาม

ผลลัพธ์: COUNT(*)เร็วขึ้น 7%

ดูกราฟ: กราฟมาตรฐาน

คำแนะนำของฉันคือใช้: SELECT COUNT(*) FROM table


1
FYI ยังมีวิธีทั่วไปในการนับด้วยCOUNT(1)น่าจะน่าสนใจที่จะดูเกณฑ์มาตรฐานที่นั่น ...
Sliq

4

ลองสิ่งนี้:

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

@lepe ฉันขอโทษ ฉันหมายความว่ามันดีจริงๆถ้ามีคนที่ลงคะแนนให้คำอธิบายว่าทำไมเขา / เธอถึงทำอย่างนั้นทุกคนจะได้เรียนรู้บางอย่างเกี่ยวกับเรื่องนี้
bayuah

1
สิ่งนี้จะให้คำตอบโดยประมาณแก่คุณอย่างรวดเร็ว หากคุณต้องการคำตอบที่แน่นอนคุณต้องดำเนินการselect count(*) from table_nameหรืออย่างอื่น dba.stackexchange.com/questions/151769/…
Programster

@Programster ขอบคุณครับ. ยังดีกว่าทิ้งฉันไว้ในความมืดเกือบปี
bayuah

1
@bayuah ฉันไม่แน่ใจว่าคุณหมายถึงอะไรในความคิดเห็นล่าสุดของคุณ ฉันสามารถสันนิษฐานได้ว่าคุณคิดว่าฉันเป็นคนที่โหวตคำตอบของคุณซึ่งฉันไม่ใช่
Programster

1
@ โปรแกรมไม่ฉันขอโทษฉันไม่ได้หมายความอย่างนั้น ฉันหมายถึงขอบคุณสำหรับคำอธิบายของคุณดังนั้นฉันจึงสามารถคาดเดาได้ว่า Downvoter อาจคิดอย่างไรเมื่อเขา / เธอทำเช่นนั้น
bayuah

3

บางทีคุณอาจต้องการพิจารณาทำไฟล์SELECT max(Id) - min(Id) + 1. สิ่งนี้จะใช้ได้ก็ต่อเมื่อรหัสของคุณเป็นลำดับและแถวจะไม่ถูกลบ อย่างไรก็ตามมันเร็วมาก


3
ระวัง: บางครั้งเซิร์ฟเวอร์ใช้ค่าการเพิ่มอัตโนมัติ> 1 (เพื่อเหตุผลในการสำรองข้อมูล) ดังนั้นวิธีนี้จึงดี แต่คุณควรตรวจสอบการกำหนดค่า DB ของคุณก่อน
Alex

1

EXPLAIN SELECT id FROM ....ทำเคล็ดลับให้ฉัน และฉันเห็นจำนวนแถวใต้rowsคอลัมน์ของผลลัพธ์


0

ฉันจัดการตารางให้กับรัฐบาลเยอรมันซึ่งบางครั้งมีบันทึกถึง 60 ล้านรายการ

และเราต้องรู้หลาย ๆ ครั้งของแถวทั้งหมด

ดังนั้นเราโปรแกรมเมอร์ฐานข้อมูลจึงตัดสินใจว่าในทุกตารางจะมีการบันทึกข้อมูลที่บันทึกไว้เสมอ เราอัปเดตหมายเลขนี้โดยขึ้นอยู่กับแถว INSERT หรือ DELETE

เราลองวิธีอื่นทั้งหมดแล้ว นี่เป็นวิธีที่เร็วที่สุด


1
และรายละเอียดว่าคุณอัปเดตแถวนั้นอย่างไร ซึ่งหมายความว่าแม้ว่าจะมีการออกแบบที่ผิดพลาดไปยังตารางโดยที่ทุกแถวจะต้องใช้ int ที่สูญเปล่าในการเดินทาง
Drew

5
เออมันโง่จริงๆฮ่า ๆ ทุกการสืบค้นคุณจะต้องละเว้นแถวแรก ฉันจะสร้างตารางผลรวมและเติมข้อมูลตามทริกเกอร์ ตารางผู้ใช้ในการแทรกอัปเดตตารางผลรวม ตารางผู้ใช้ลบอัปเดตตารางผลรวม
HTMLGuy

-1

คำสั่ง count (*) ที่มีเงื่อนไขบนคีย์หลักส่งคืนการนับแถวเร็วขึ้นมากสำหรับฉันที่หลีกเลี่ยงการสแกนแบบเต็มตาราง

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

นี่เร็วกว่าสำหรับฉันมาก

SELECT COUNT(*) FROM ...
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.