วิธีที่เร็วที่สุดในการตรวจสอบว่ามีบันทึกอยู่หรือไม่


143

ตามที่ชื่อแนะนำ ... ฉันกำลังพยายามหาวิธีที่เร็วที่สุดโดยมีค่าใช้จ่ายน้อยที่สุดเพื่อดูว่ามีระเบียนอยู่ในตารางหรือไม่

แบบสอบถามตัวอย่าง:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

สมมติว่า?มีการสลับกับ'TB100'... ทั้งแบบสอบถามที่หนึ่งและที่สองจะส่งกลับผลลัพธ์เดียวกัน (พูดว่า ... 1สำหรับการสนทนานี้) แบบสอบถามล่าสุดจะส่งคืน'TB100'ตามที่คาดไว้หรือไม่มีสิ่งใดหากidไม่มีอยู่ในตาราง

จุดประสงค์คือการคิดออกว่าidอยู่ในตารางหรือไม่ มิฉะนั้นโปรแกรมจะแทรกเร็กคอร์ดต่อไปถ้าเป็นเช่นนั้นโปรแกรมจะข้ามหรือดำเนินการค้นหา UPDATE ตามตรรกะของโปรแกรมอื่นที่อยู่นอกขอบเขตของคำถามนี้

ไหนเร็วกว่าและมีค่าใช้จ่ายน้อยลง (ซึ่งจะถูกทำซ้ำหลายหมื่นครั้งต่อการรันโปรแกรมและจะทำงานหลายครั้งต่อวัน)

(การรันเคียวรีนี้เทียบกับ M $ SQL Server จาก Java ผ่านทาง M $ ไดร์เวอร์ JDBC ที่ให้ไว้)


1
นี่อาจขึ้นอยู่กับฐานข้อมูล ตัวอย่างเช่นการนับ Postgres ค่อนข้างช้า
Mike Christensen

ขออภัยนี่คือ Java ที่พูดคุยกับ M $ SQL ผ่านทางไดรเวอร์ jdbc ฉันจะอัพเดท OP ของฉัน
SnakeDoc

2
นอกจากนี้ยังมี
Nikola Markovinović

@Nikola Markovinović: คุณจะใช้มันอย่างไรในกรณีนี้
zerkms

5
@zerkms ขึ้นอยู่กับบริบท หากอยู่ในขั้นตอนการจัดเก็บมันจะเป็นif exists(select null from products where id = @id); select case when exists (...) then 1 else 0 endถ้าในแบบสอบถามที่เรียกว่าโดยตรงจากลูกค้า
Nikola Markovinović

คำตอบ:


170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; จะมีประสิทธิภาพสูงกว่าข้อเสนอแนะของคุณทั้งหมดเนื่องจากจะยุติการทำงานหลังจากพบระเบียนแรก


5
เครื่องมือเพิ่มประสิทธิภาพไม่คำนึงถึงตัวเองเมื่อค้นหาผ่าน PK (หรือคีย์เฉพาะอื่น ๆ )
zerkms

3
เขาไม่ได้ระบุว่าเป็น PK แต่ถ้าเป็นเช่นนั้นใช่เครื่องมือเพิ่มประสิทธิภาพจะคำนึงถึงสิ่งนั้น
Declan_K

3
@Declan_K: ดูเหมือนว่าเวทย์มนตร์ของฉันจะล้มเหลวในกรณีนี้และคอลัมน์ที่ชื่อidไม่ใช่ PK ดังนั้น +1 ตามคำแนะนำของคุณ
zerkms

4
ถ้าไม่ใช่ PK ฉันก็จะแนะนำให้แน่ใจว่ามีดัชนีในคอลัมน์นั้น มิฉะนั้นแบบสอบถามจะต้องทำการสแกนตารางแทนการค้นหาตารางที่รวดเร็วขึ้น
ซีดี Jorgensen

4
ฉันคิดว่าเราควรพิจารณา @ nenad-zivkovic คำตอบสำหรับคำถามนี้
Giulio Caccin

193

EXISTS(หรือNOT EXISTS) ได้รับการออกแบบมาเป็นพิเศษสำหรับตรวจสอบว่ามีบางสิ่งอยู่หรือไม่ดังนั้นจึงควรเป็นตัวเลือกที่ดีที่สุด มันจะหยุดในแถวแรกที่ตรงกันดังนั้นจึงไม่จำเป็นต้องมีTOPประโยคและไม่ได้เลือกข้อมูลใด ๆ ดังนั้นจึงไม่มีค่าใช้จ่ายในขนาดของคอลัมน์ คุณสามารถใช้SELECT *ที่นี่ - ไม่แตกต่างSELECT 1, SELECT NULLหรือSELECT AnyColumn... (คุณยังสามารถใช้การแสดงออกที่ไม่ถูกต้องเช่นSELECT 1/0และมันจะไม่ทำลาย)

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END

สิ่งนี้ไม่จำเป็นต้องเรียกใช้คำสั่ง SELECT ก่อนจากนั้นดำเนินการคำสั่ง IF EXISTS ... ทำให้เกิดค่าใช้จ่ายเพิ่มเติมและทำให้เวลาในการประมวลผลมากขึ้นหรือไม่
SnakeDoc

7
@SnakeDoc ไม่Existsทำงานร่วมกับselectในแบบที่มันออกมาทันทีที่พบแถวหนึ่ง นอกจากนี้ยังมีเพียงบันทึกการมีอยู่ของระเบียนไม่ใช่ค่าจริงในบันทึกการบันทึกความจำเป็นในการโหลดแถวจากดิสก์ (สมมติว่าเกณฑ์การค้นหาถูกจัดทำดัชนีแน่นอน) สำหรับค่าใช้จ่ายของif- คุณจะต้องใช้เวลาจิ๋วนี้อยู่ดี
Nikola Markovinović

1
@ NikolaMarkovinovićจุดที่น่าสนใจ ฉันไม่แน่ใจว่ามีดัชนีอยู่ในฟิลด์นี้หรือไม่และ SQL ตัวใหม่ของฉันไม่รู้วิธีการค้นหา ฉันกำลังทำงานกับฐานข้อมูลนี้จาก Java ผ่าน JDBC และฐานข้อมูลอยู่ในระยะไกลใน colo ที่ไหนสักแห่ง ฉันได้รับเพียงแค่ "สรุปฐานข้อมูล" ซึ่งมีรายละเอียดเฉพาะว่ามีฟิลด์ใดบ้างในแต่ละตารางประเภทและ FK หรือ PK ใด ๆ สิ่งนี้เปลี่ยนแปลงอะไรหรือไม่?
SnakeDoc

3
@SnakeDoc เพื่อหาข้อมูลเกี่ยวกับโครงสร้างของตารางรวมถึงปุ่มต่างประเทศและดัชนีวิ่งsp_help table_name ดัชนีมีความสำคัญเมื่อต้องดึงข้อมูลแถวสองสามแถวออกมาจำนวนมากเมื่อใช้งานselect topหรือexists; หากพวกเขาไม่ได้อยู่ในโปรแกรม sql จะต้องทำการสแกนตาราง นี่เป็นตัวเลือกการค้นหาตารางที่ต้องการอย่างน้อยที่สุด หากคุณไม่ได้รับอนุญาตให้สร้างดัชนีคุณจะต้องสื่อสารกับเจ้าหน้าที่ด้านเทคนิคในด้านอื่น ๆ เพื่อดูว่าพวกเขาปรับโดยอัตโนมัติหรือพวกเขาคาดหวังว่าคุณจะแนะนำดัชนี
Nikola Markovinović

1
@Konstantin คุณสามารถทำสิ่งที่ต้องการSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic

21

ไม่มีอะไรสามารถเอาชนะ -

SELECT TOP 1 1 FROM products WHERE id = 'some value';

คุณไม่จำเป็นต้องนับเพื่อทราบว่ามีข้อมูลในตารางหรือไม่ และไม่ใช้นามแฝงเมื่อไม่จำเป็น


5
ทั้งๆที่ชื่อidไม่ได้เป็นคีย์หลัก ดังนั้นแม้ว่าคุณจะไม่นับคุณยังจำเป็นต้องค้นหาระเบียนที่ตรงกันทั้งหมดอาจเป็นพัน ๆ เกี่ยวกับนามแฝง - รหัสกำลังทำงานอย่างต่อเนื่อง คุณไม่มีทางรู้ว่าคุณจะต้องกลับไปเมื่อไหร่ นามแฝงช่วยป้องกันข้อผิดพลาดรันไทม์โง่ ตัวอย่างเช่นชื่อคอลัมน์ที่ไม่ซ้ำซึ่งไม่ต้องการนามแฝงนั้นไม่ซ้ำกันอีกต่อไปเพราะมีคนสร้างคอลัมน์ชื่อเดียวกันในอีกตารางหนึ่ง
Nikola Markovinović

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

3
ฉันไม่รู้ว่าทำไมฉันถึงจำศัพท์aliasingได้ qualifyingคำที่ถูกต้องคือ นี่คือคำอธิบายอีกต่อไปโดยอเล็กซ์ Kuznetzov เกี่ยวกับคำสั่งตารางเดียว - มันเป็นตารางเดียวในขณะนี้ แต่ต่อมาเมื่อมีการค้นพบข้อผิดพลาดและคุณพยายามที่จะระงับน้ำท่วมลูกค้าเป็นกังวลคุณเข้าร่วมตารางอื่นเพียงเพื่อเผชิญกับข้อผิดพลาด - ข้อความที่แก้ไขได้อย่างง่ายดาย แต่ไม่ได้อยู่ในช่วงเวลาที่เหงื่อตกนี้ ข้อผิดพลาดความทรงจำไม่เคยที่จะออกจากคอลัมน์ ...
นิโคลาMarkovinović

1
ไม่สามารถเพิกเฉยได้ตอนนี้ ขอบคุณ !! :)
AgentSQL

15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

วิธีการนี้จะส่งคืนบูลีนสำหรับคุณ


1
อาจละเว้นคำสั่ง Top และคำสั่ง * เพื่อทำให้เร็วขึ้นเล็กน้อยขณะที่ Exist จะออกเมื่อพบระเบียนดังนั้นสิ่งเช่นนี้: SELECT CASE เมื่อมีอยู่ (เลือก 1 จาก dbo [YourTable] WHERE [YourColumn] = [YourValue]) นักแสดงจากนั้น (1 ในขณะที่บิต) นักแสดงอื่น ๆ (0 เท่าในตอนนี้) END
Stefan Zvonar

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


7

อย่าคิดว่ามีใครพูดถึงมัน แต่ถ้าคุณแน่ใจว่าข้อมูลจะไม่เปลี่ยนแปลงภายใต้คุณคุณอาจต้องการใช้คำใบ้ NoLock เพื่อให้แน่ใจว่ามันจะไม่ถูกบล็อกเมื่ออ่าน

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END

3
SELECT COUNT(*) FROM products WHERE products.id = ?;

นี่คือโซลูชันฐานข้อมูลเชิงสัมพันธ์ข้ามที่ทำงานในฐานข้อมูลทั้งหมด


7
แต่คุณบังคับให้ฐานข้อมูลห่วงมากกว่าระเบียนทั้งหมดช้ามากบนโต๊ะขนาดใหญ่
เอเอ็มดี

@amd สนใจที่จะอธิบายว่าทำไม
UmNyobe

@amd ความคิดเห็นของคุณสมเหตุสมผลแล้ว แบบสอบถามนี้เป็นการค้นหาทั้งหมดมากกว่าการค้นหาใด ๆ
UmNyobe

1

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

SELECT distinct 1 products.id FROM products WHERE products.id = ?;

0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;

2
อาจเป็นไปได้ว่ารหัสของคุณใช้งานได้ดี แต่จะดีกว่าถ้าคุณเพิ่มข้อมูลเพิ่มเติมเพื่อให้เข้าใจได้ง่ายขึ้น
idmean

0

ฉันเคยใช้สิ่งนี้ในอดีตและไม่ต้องใช้การสแกนแบบเต็มตารางเพื่อดูว่ามีบางสิ่งอยู่ มันเร็วมาก ...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             

0

สำหรับผู้ที่สะดุดกับสิ่งนี้จากพื้นหลังของ MySQL หรือ Oracle - MySQL รองรับส่วนคำสั่ง LIMIT เพื่อเลือกจำนวนเรคคอร์ดที่ จำกัด ในขณะที่ Oracle ใช้ ROWNUM

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