SQL: วิธีการตรวจสอบอย่างถูกต้องว่ามีการบันทึกอยู่


207

ขณะอ่านเอกสารเกี่ยวกับการปรับแต่ง SQL ฉันพบสิ่งนี้:

SELECT COUNT(*) :

  • นับจำนวนแถว
  • มักใช้อย่างไม่เหมาะสมเพื่อตรวจสอบการมีอยู่ของระเบียน

คือSELECT COUNT(*)จริงๆที่ไม่ดีที่?

วิธีที่เหมาะสมในการตรวจสอบการมีอยู่ของบันทึกคืออะไร?

คำตอบ:


252

ควรใช้วิธีใดวิธีหนึ่งต่อไปนี้:

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

ทางเลือกแรกไม่ควรให้ผลลัพธ์หรือผลลัพธ์เดียวการนับครั้งที่สองควรเป็นศูนย์หรือหนึ่งรายการ

เอกสารที่คุณใช้อยู่มีอายุเท่าไหร่ แม้ว่าคุณจะได้อ่านคำแนะนำที่ดี แต่ตัวเพิ่มประสิทธิภาพการสืบค้นส่วนใหญ่ในการปรับให้เหมาะสมที่สุดของ RDBMS SELECT COUNT(*)อยู่แล้วดังนั้นแม้ว่าจะมีความแตกต่างในทางทฤษฎี (และฐานข้อมูลเก่า) แต่คุณไม่ควรสังเกตเห็นความแตกต่างในทางปฏิบัติ


1
ฉันจะชี้แจงว่าฉันตั้งใจ "คีย์ที่ไม่ซ้ำ" กับประโยค "key = value" แต่นอกเหนือจากนั้นฉันยังอยู่หลังคำตอบของฉัน
Martin Schapendonk

1
ตกลง. ด้วยหลักฐานที่แน่นอนแบบสอบถามจะกลับมาเพียงหนึ่งหรือศูนย์บันทึก แต่: คำถามไม่ได้ จำกัด เฉพาะคอลัมน์ที่ไม่ซ้ำ นอกจากนี้: การนับการสืบค้นครั้งที่ 2 (1) เทียบเท่ากับการนับ (*) จาก POV เชิงปฏิบัติ
Martin Ba

1
คำถามบอกว่า "อะไรคือวิธีที่เหมาะสมในการตรวจสอบการมีอยู่ของระเบียน A" ฉันตีความว่าเป็นเอกพจน์เช่นเดียวกับใน: 1 บันทึก ความแตกต่างระหว่าง count (*) และ count (1) นั้นครอบคลุมโดยคำตอบของฉัน ฉันชอบ count (1) เพราะมันไม่ได้ขึ้นอยู่กับการใช้งานของ RDBMS ที่เฉพาะเจาะจง
Martin Schapendonk

192

ฉันไม่ต้องการใช้ฟังก์ชัน Count เลย:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

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

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END

Generaly เราใช้มัน (การตรวจสอบ) เมื่อต้องการทำอะไรบางอย่างแล้วคำตอบของคุณจะสมบูรณ์มากขึ้น
Abner Escócio

เป็นการดีที่จะกล่าวถึงโดยใช้ T-SQL
Bronek

20

คุณสามารถใช้ได้:

SELECT 1 FROM MyTable WHERE <MyCondition>

ถ้าไม่มีระเบียนที่ตรงกับเงื่อนไขชุดระเบียนที่ได้จะว่างเปล่า


คุณหมายถึง TOP 1 หรือเปล่า -> (เลือก 1 อันดับแรกจาก MyTable WHERE <MyCondition>)
Jacob

6
ไม่ฉันหมายถึง "1"
Cătălin Pitiș

1
หากต้องการเปิดใช้งานเครื่องมือเพิ่มประสิทธิภาพการสืบค้นแม้จะรู้ว่าคุณไม่ต้องอ่าน / ต้องการชุดข้อมูลที่เหลืออยู่คุณควรระบุ SELECT TOP 1 1 จาก ... WHERE ... (หรือใช้คำแนะนำแบบสอบถามที่เหมาะสมสำหรับ RDBS ของคุณ)
eFloh

3
ตัวดำเนินการ Exists พยายามดึงข้อมูลขั้นต่ำเพียงเล็กน้อยดังนั้นการเพิ่ม TOP 1 ไม่ได้ทำอะไรนอกจากเพิ่ม 5 อักขระให้กับขนาดแบบสอบถาม - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
AquaAlex

13

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


3
หากแบบสอบถาม "ตรวจสอบการมีอยู่" ใด ๆ ส่งคืนมากกว่าหนึ่งแถวฉันคิดว่ามีประโยชน์มากกว่าถ้าคุณตรวจสอบคำสั่งย่อย WHERE ของคุณแทนการ จำกัด จำนวนผลลัพธ์
Martin Schapendonk

2
ฉันคิดว่า Limit ถูกใช้ใน Oracle และไม่ใช่ใน SQL Server
Shantanu Gupta

7
ฉันกำลังพิจารณากรณีที่พวกเขาสามารถเป็นหลายแถวได้อย่างถูกกฎหมาย - ที่คำถามคือ: "มี (หนึ่งหรือมากกว่า) แถวที่ตรงตามเงื่อนไขนี้หรือไม่?" ในกรณีนี้คุณไม่ต้องการดูพวกเขาทั้งหมดเพียงคนเดียว
JesseW

1
@Shantanu - ฉันรู้ว่านั่นเป็นเหตุผลที่ฉันเชื่อมโยงกับบทความวิกิพีเดียที่อธิบายถึงรูปแบบอื่น ๆ
JesseW

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

จะวนซ้ำผ่านระเบียนทั้งหมด นี่คือเหตุผลที่ไม่ดีที่จะใช้สำหรับการบันทึก

ฉันจะใช้

SELECT TOP 1 * FROM MyTable WHERE ...

หลังจากค้นหา 1 ระเบียนมันจะยุติการวนซ้ำ


ในกรณีที่SELECT TOP 1มันจะยุติจริง ๆ หลังจากการค้นหาหนึ่งหรือมันจะยังคงหาทั้งหมดที่จะบอกว่าเป็นที่หนึ่ง TOP?
Eirik H

3
PS: เพื่อให้แน่ใจว่าฉันเสมอIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H

ตัวดำเนินการ Star จะบังคับให้ DBMS เข้าถึงดัชนีคลัสเตอร์แทนเพียงดัชนี (es) ที่จำเป็นสำหรับเงื่อนไขการเข้าร่วมของคุณ ดังนั้นจะดีกว่าถ้าใช้ valua คงที่เช่นเลือก top 1 1 .... ที่จะคืนค่า 1 หรือ DB-Null ขึ้นอยู่กับเงื่อนไขคือการแข่งขันหรือไม่
eFloh

มันดีนะ. ฉันชอบอันแรก
isxaker

10

คุณสามารถใช้ได้:

SELECT COUNT(1) FROM MyTable WHERE ... 

หรือ

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

สิ่งนี้จะมีประสิทธิภาพมากกว่าที่SELECT *คุณเพียงแค่เลือกค่า 1 สำหรับแต่ละแถวแทนที่จะเป็นฟิลด์ทั้งหมด

นอกจากนี้ยังมีความแตกต่างเล็กน้อยระหว่าง COUNT (*) และ COUNT (ชื่อคอลัมน์):

  • COUNT(*) จะนับแถวทั้งหมดรวมถึงโมฆะ
  • COUNT(column name)จะนับเฉพาะชื่อคอลัมน์ที่ไม่เป็นโมฆะเท่านั้น

2
คุณกำลังเข้าใจผิดว่า DBMS จะตรวจสอบคอลัมน์เหล่านั้นอย่างใด ความแตกต่างของประสิทธิภาพระหว่างcount(1)และcount(*)จะแตกต่างกันใน DBMS ที่ทำให้สมองตายที่สุดเท่านั้น
paxdiablo

2
ไม่ฉันกำลังบอกว่าคุณพึ่งพารายละเอียดการใช้งานจริงเมื่อคุณระบุว่าจะมีประสิทธิภาพมากขึ้น หากคุณต้องการให้แน่ใจว่าคุณได้รับประสิทธิภาพที่ดีที่สุดคุณควรทำโพรไฟล์เพื่อการใช้งานเฉพาะโดยใช้ข้อมูลตัวแทนหรือเพียงแค่ลืมมันไปเลย สิ่งอื่นใดที่อาจทำให้เข้าใจผิดและสามารถเปลี่ยนแปลงอย่างมากเมื่อย้าย (ตัวอย่าง) จาก DB2 เป็น MySQL
paxdiablo

1
ฉันต้องการทำให้ชัดเจนฉันไม่ได้ปฏิเสธคำตอบของคุณ มันมีประโยชน์ เพียงบิตผมไม่เห็นด้วยกับการเรียกร้องเป็นอย่างมีประสิทธิภาพตั้งแต่ที่เราได้ทำการประเมินผลใน DB2 / z และพบว่าไม่มีความแตกต่างระหว่างจริงและcount(*) count(1)ไม่ว่าจะเป็นกรณีสำหรับDBMS อื่น ๆฉันไม่สามารถพูดได้
paxdiablo

3
"สิ่งอื่นใดที่อาจทำให้เข้าใจผิดและสามารถเปลี่ยนแปลงได้อย่างมากเมื่อมีการย้าย (ตัวอย่าง) จาก DB2 เป็น MySQL"คุณมีแนวโน้มที่จะถูกกัดโดยการลดประสิทธิภาพของ SELECT COUNT (*) เมื่อย้าย DBMS มากกว่าความแตกต่างของการใช้งานใน SELECT 1 หรือ COUNT (1) ฉันเชื่อมั่นในการเขียนโค้ดที่ชัดเจนที่สุดว่าคุณต้องการบรรลุอะไรมากกว่าการพึ่งพาตัวเพิ่มประสิทธิภาพหรือคอมไพเลอร์เพื่อเริ่มต้นพฤติกรรมที่คุณต้องการ
Winston Smith

1
คำสั่งที่ทำให้เข้าใจผิด "COUNT (*)" หมายถึง 'นับจำนวนแถว' การหยุดแบบเต็ม มันไม่จำเป็นต้องเข้าถึงคอลัมน์ใด ๆ และในกรณีส่วนใหญ่จะไม่จำเป็นต้องเข้าถึงแถวเองเนื่องจากการนับดัชนีเฉพาะใด ๆ ก็เพียงพอแล้ว
James Anderson

9

คุณสามารถใช้ได้:

SELECT 1 FROM MyTable WHERE... LIMIT 1

ใช้select 1เพื่อป้องกันการตรวจสอบฟิลด์ที่ไม่จำเป็น

ใช้LIMIT 1 เพื่อป้องกันการตรวจสอบแถวที่ไม่จำเป็น


3
จุดดี แต่ขีด จำกัด ทำงานบน MySQL และ PostgreSQL งานบน SQL Server คุณควรจดคำตอบของคุณไว้
Leo Gurdian


0

ตัวเลือกอื่น ๆ :

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.