วิธีที่ดีที่สุดในการทดสอบว่าแถวมีอยู่ในตาราง MySQL หรือไม่


337

ฉันพยายามค้นหาว่ามีแถวในตารางหรือไม่ การใช้ MySQL จะดีกว่าไหมถ้าทำเคียวรีเช่นนี้

SELECT COUNT(*) AS total FROM table1 WHERE ...

และตรวจสอบเพื่อดูว่ายอดรวมไม่เป็นศูนย์หรือดีกว่าที่จะทำแบบสอบถามเช่นนี้:

SELECT * FROM table1 WHERE ... LIMIT 1

และตรวจสอบเพื่อดูว่ามีการส่งคืนแถวหรือไม่

ในแบบสอบถามทั้งสองข้อ WHERE ใช้ดัชนี

คำตอบ:


470

คุณสามารถลองEXISTS:

SELECT EXISTS(SELECT * FROM table1 WHERE ...)

และตามเอกสารคุณสามารถSELECTทำอะไรก็ได้

ตามปกติแล้วแบบสอบถามย่อย EXISTS เริ่มต้นด้วย SELECT * แต่สามารถเริ่มต้นด้วย SELECT 5 หรือ SELECT column1 หรืออะไรก็ได้ MySQL เพิกเฉยรายการ SELECT ในแบบสอบถามย่อยดังกล่าวดังนั้นจึงไม่แตกต่างกัน


30
...EXISTS( SELECT 1/0 FROM someothertable)การทดสอบด้วย สำหรับ SQL Server & Oracle - ไม่ทำให้การใช้ *, 1 หรือ NULL ไม่แตกต่างกันเนื่องจาก EXISTS จะทดสอบเฉพาะบูลีนที่อิงกับ 1+ ของเกณฑ์ WHERE
OMG Ponies

77
พวกมันบอกว่าถูกต้องในเอกสารที่เชื่อมโยงกับในคำตอบนี้วรรค 2 "ตามเนื้อผ้าแบบสอบถามย่อย EXISTS เริ่มต้นด้วย SELECT * แต่มันอาจเริ่มต้นด้วย SELECT 5 หรือ SELECT คอลัมน์ 1 หรืออะไรก็ได้ MySQL ไม่สนใจรายการ SELECT ดังกล่าว แบบสอบถามย่อยจึงไม่สร้างความแตกต่าง "
mpen

12
@ChrisThompson: จะเกิดอะไรขึ้นเมื่อคำสั่งถูกดำเนินการ ฉันหมายถึงชุดผลลัพธ์มีอะไรบ้าง
Ashwin

13
@Ashwin มันประกอบด้วย 0 (ไม่มีอยู่) หรือ 1 (มีอยู่)
fedorqui 'ดังนั้นหยุดทำอันตราย'

10
ฉันคิดว่าข้อความค้นหาของคุณฟุ่มเฟือยฉันทดสอบและแบบสอบถามSELECT 1 FROM table1 WHERE col = $var LIMIT 1นี้เร็วกว่าข้อความค้นหาของคุณ ดังนั้นข้อได้เปรียบของแบบสอบถามของคุณคืออะไร?
Shafizadeh

182

ฉันได้ทำวิจัยเกี่ยวกับเรื่องนี้เมื่อเร็ว ๆ นี้ วิธีการนำไปใช้จะต้องแตกต่างกันถ้าเขตข้อมูลเป็นเขตข้อมูล TEXT ซึ่งเป็นเขตข้อมูลที่ไม่ซ้ำกัน

ฉันทำการทดสอบด้วยฟิลด์ TEXT พิจารณาข้อเท็จจริงที่ว่าเรามีตารางที่มี 1M รายการ 37 รายการเท่ากับ 'บางสิ่ง':

  • SELECT * FROM test WHERE texte LIKE '%something%' LIMIT 1ด้วย mysql_num_rows() : 0.039061069488525s (เร็วกว่า)
  • SELECT count(*) as count FROM test WHERE text LIKE '%something% : 16.028197050095s
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%') : 0.87045907974243s
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%' LIMIT 1) : 0.044898986816406s

แต่ตอนนี้ด้วยฟิลด์ BIGINT PK มีเพียงหนึ่งรายการเท่านั้นที่เท่ากับ '321321':

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1ด้วย mysql_num_rows() : 0.0089840888977051s
  • SELECT count(*) as count FROM test2 WHERE id ='321321' : 0.00033879280090332s
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321') : 0.00023889541625977 วินาที
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321' LIMIT 1): 0.00020313262939453 วินาที (เร็วกว่า)

2
ขอบคุณสำหรับคำตอบเพิ่มเติม คุณพบความแตกต่างในเวลาระหว่างสองตัวเลือกที่เร็วที่สุดสำหรับเขตข้อมูล TEXT ที่จะค่อนข้างสอดคล้องกันหรือไม่? ความแตกต่างนั้นดูไม่ใหญ่นักและการใช้ SELECT EXISTS (SELECT 1 ... LIMIT 1) นั้นค่อนข้างดีทั้งสองกรณี
เบอร์นาร์ดเฉิน

1
คุณถูกต้องความแตกต่างนั้นไม่สำคัญนักเกี่ยวกับผลลัพธ์อื่น ๆ ที่เกี่ยวข้องกับฟิลด์ข้อความ อย่างไรก็ตามอาจใช้การค้นหาได้ดีขึ้นSELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
Laurent W.

ฉันพยายามที่ mysql และในกรณีที่คุณใช้select 1 ... limit 1มันไม่มีประโยชน์ที่จะล้อมรอบด้วยตัวเลือกที่มีอยู่
Adrien Horgnies

4
@LittleNooby มีความแตกต่าง SELECT EXISTS ... ให้ค่าจริงและเท็จ (1 หรือ 0) ในขณะที่ SELECT 1 ... ให้ 1 หรือว่างเปล่า มีความแตกต่างเล็กน้อยระหว่างค่าเท็จและชุดที่ว่างเปล่าขึ้นอยู่กับสถานการณ์ของคุณ
Quickpick

@ LittleNooby ทำให้เป็นจุดที่ยอดเยี่ยมซึ่งง่ายต่อการมองข้าม ที่ขาดหายไปในการทดสอบเวลาข้างต้นคือSELECT 1 FROM test WHERE ...โดยไม่ต้องSELECT EXISTSไปรอบ ๆ มัน คงจะเป็นผมที่เร็วขึ้น
ToolmakerSteve

27

ตัวอย่างสั้น ๆ ของคำตอบของ @ ChrisThompson

ตัวอย่าง:

mysql> SELECT * FROM table_1;
+----+--------+
| id | col1   |
+----+--------+
|  1 | foo    |
|  2 | bar    |
|  3 | foobar |
+----+--------+
3 rows in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 1) |
+--------------------------------------------+
|                                          1 |
+--------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 9);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 9) |
+--------------------------------------------+
|                                          0 |
+--------------------------------------------+
1 row in set (0.00 sec)

ใช้นามแฝง:

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1) AS mycheck;
+---------+
| mycheck |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)

18

ในการวิจัยของฉันฉันสามารถค้นหาผลลัพธ์ที่ได้จากการติดตามความเร็ว

select * from table where condition=value
(1 total, Query took 0.0052 sec)

select exists(select * from table where condition=value)
(1 total, Query took 0.0008 sec)

select count(*) from table where condition=value limit 1) 
(1 total, Query took 0.0007 sec)

select exists(select * from table where condition=value limit 1)
(1 total, Query took 0.0006 sec) 

12

ฉันรู้สึกว่ามันคุ้มค่าที่จะชี้ให้เห็นแม้ว่ามันจะถูกสัมผัสในความคิดเห็นว่าในสถานการณ์นี้:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

ดีกว่า:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

นี่เป็นเพราะแบบสอบถามแรกสามารถทำได้โดยดัชนีในขณะที่สองต้องค้นหาแถว (เว้นแต่คอลัมน์ของตารางทั้งหมดจะอยู่ในดัชนีที่ใช้)

การเพิ่มส่วนLIMITคำสั่งอนุญาตให้เอ็นจินหยุดหลังจากค้นหาแถวใด ๆ

แบบสอบถามแรกควรเปรียบเทียบได้กับ:

SELECT EXISTS(SELECT * FROM my_table WHERE *indexed_condition*)

ซึ่งส่งสัญญาณเดียวกันไปยังเครื่องยนต์ (1 / * ไม่สร้างความแตกต่างที่นี่) แต่ฉันยังคงเขียน 1 เพื่อเสริมสร้างนิสัยเมื่อใช้EXISTS:

SELECT EXISTS(SELECT 1 FROM my_table WHERE *indexed_condition*)

การเพิ่มการEXISTSห่อหากคุณต้องการผลตอบแทนชัดเจนเมื่อไม่มีแถวที่ตรงกัน


4

ขอแนะนำให้คุณอย่าใช้Countเพราะการนับทำให้การโหลดเพิ่มพิเศษสำหรับการใช้ db SELECT 1และส่งคืน1หากบันทึกของคุณตรงนั้นมิฉะนั้นจะส่งคืนค่าว่างและคุณสามารถจัดการได้


2

ข้อความค้นหาCOUNTเร็วกว่าแม้ว่าอาจจะไม่ชัดเจน แต่เท่าที่ได้รับผลลัพธ์ที่ต้องการทั้งคู่ก็เพียงพอแล้ว


4
อย่างไรก็ตามนี่คือฐานข้อมูลที่เฉพาะเจาะจง COUNT (*) ทราบว่าช้าใน PostgreSQL ดีกว่าที่จะเลือกคอลัมน์ PK และดูว่ามันจะส่งกลับแถวใด ๆ
BalusC

3
COUNT (*) จะช้าใน InnoDB แม้ว่า
Will

2

บางครั้งมันก็ค่อนข้างสะดวกในการรับคีย์หลักที่เพิ่มขึ้นอัตโนมัติ ( id) ของแถวหากมีอยู่และ0หากไม่มี

นี่คือวิธีการนี้สามารถทำได้ในการค้นหาเดียว:

SELECT IFNULL(`id`, COUNT(*)) FROM WHERE ...

ทำไมไม่ใช้IFNULL(id, 0)ที่นี่แทนที่จะเป็นCOUNT(*)?
อีธานโฮเฮนเซ


-1

COUNT(1)ฉันไปกับ มันเร็วกว่าCOUNT(*)เพราะCOUNT(*)การทดสอบเพื่อดูว่าอย่างน้อยหนึ่งคอลัมน์ในแถวนั้นคือ! = NULL คุณไม่จำเป็นต้องทำเช่นนั้นโดยเฉพาะอย่างยิ่งเพราะคุณมีเงื่อนไขอยู่แล้ว ( WHEREข้อ) COUNT(1)แทนการทดสอบความถูกต้องของ1ซึ่งจะถูกต้องเสมอและใช้เวลาน้อยมากในการทดสอบ


8
-1 นี่มันผิด COUNT (*) ไม่ได้ดูค่าของคอลัมน์ - เพียงนับจำนวนแถว ดูคำตอบของฉันที่นี่: stackoverflow.com/questions/2876909/…
Mark Byers

6
COUNT () ช้ากว่า EXISTS มากเนื่องจาก EXISTS สามารถกลับมาได้เมื่อพบแถวแรก
Will

-1

หรือคุณสามารถแทรกส่วนของ sql raw กับเงื่อนไขดังนั้นฉันจึงมี 'conditions' => array ('Member.id ไม่ได้อยู่ใน (SELECT Membership.member_id จากการเป็นสมาชิก AS Membership)')


-2

COUNT(*) ได้รับการปรับให้เหมาะสมใน MySQL ดังนั้นข้อความค้นหาเดิมน่าจะเร็วกว่า


2
คุณหมายถึงการปรับให้เหมาะสมที่ MyISAM มีสำหรับการเลือกการนับสำหรับทั้งตารางหรือไม่? ฉันไม่คิดว่าจะช่วยได้หากมีเงื่อนไขว่าอยู่ที่ไหน
เบอร์นาร์ดเฉิน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.