วิธีที่มีประสิทธิภาพที่สุดในการลบแถวจำนวนมากออกจาก postgres


23

ฉันสงสัยว่าวิธีที่มีประสิทธิภาพมากที่สุดคือการลบจำนวนแถวขนาดใหญ่ออกจาก PostgreSQL กระบวนการนี้จะเป็นส่วนหนึ่งของงานที่เกิดซ้ำทุกวันเพื่อนำเข้าข้อมูลจำนวนมาก (ส่วนที่แทรก + ลบ) ลงในตาราง อาจมีหลายพันแถวที่อาจลบเป็นล้านแถว

ฉันมีไฟล์ของคีย์หลักหนึ่งรายการต่อบรรทัด ตัวเลือกสองตัวที่ฉันคิดว่ามีอยู่ตามลำดับด้านล่าง แต่ฉันไม่ทราบ / เข้าใจ internals ของ PostgreSQL มากพอที่จะทำการตัดสินใจอย่างชาญฉลาดซึ่งจะดีที่สุด

  • ดำเนินการDELETEค้นหาสำหรับแต่ละแถวในไฟล์โดยใช้WHEREคีย์หลักอย่างง่าย(หรือจัดกลุ่มการลบเป็นกลุ่มnโดยใช้ส่วนIN()คำสั่ง)
  • นำเข้าคีย์หลักเข้าสู่ตารางชั่วคราวโดยใช้COPYคำสั่งแล้วลบออกจากตารางหลักโดยใช้การเข้าร่วม

ข้อเสนอแนะใด ๆ จะได้รับการชื่นชมมาก!


1
ตอบคำถามเดียวกันได้ในรายละเอียดเพิ่มเติมได้ที่นี่: stackoverflow.com/a/8290958
Simon

คำตอบ:


25

ตัวเลือกที่สองของคุณสะอาดกว่าและจะทำงานได้ดีพอที่จะทำให้คุ้มค่า ทางเลือกของคุณคือการสร้างข้อความค้นหาขนาดใหญ่ซึ่งจะค่อนข้างเจ็บปวดในการวางแผนและดำเนินการ โดยทั่วไปคุณจะต้องดีกว่าถ้าปล่อยให้ PostgreSQL ทำงานที่นี่ โดยทั่วไปแล้วฉันได้พบการอัปเดตเกี่ยวกับแถวหลายหมื่นแถวในลักษณะที่คุณกำลังอธิบายเพื่อให้มีประสิทธิภาพเพียงพอ แต่มีสิ่งสำคัญอย่างหนึ่งที่ควรหลีกเลี่ยง

วิธีที่จะทำคือการใช้ตัวเลือกและเข้าร่วมในการลบของคุณ

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

ไม่ว่าในกรณีใด ๆ คุณควรปฏิบัติตามตารางขนาดใหญ่ดังนี้

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

ซึ่งมักจะทำให้เกิดการวนซ้ำ antijoin ซึ่งจะทำให้ประสิทธิภาพค่อนข้างมีปัญหา หากคุณต้องไปเส้นทางนั้นให้ทำเช่นนี้แทน:

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQL นั้นค่อนข้างดีในการหลีกเลี่ยงแผนการที่ไม่ดี แต่ยังมีกรณีที่เกี่ยวข้องกับการรวมภายนอกซึ่งสามารถสร้างความแตกต่างอย่างมากระหว่างแผนการดีและไม่ดี

นี่เป็นสิ่งที่ไกลออกไปเล็กน้อย แต่ฉันคิดว่ามันควรค่าแก่การกล่าวถึงเพราะมันง่ายแค่ไหนที่จะไปจาก IN ถึง NOT IN และดูรถถังประสิทธิภาพแบบสอบถาม


นั่นช่วยได้มากขอบคุณ! อย่างไรก็ตามฉันพบว่าการใช้ "การรวมข้อความค้นหา" นั้นมีประสิทธิภาพมากกว่าในกรณีนี้โดยเฉพาะ เช่น IN ( select id from foo except select id from rows_to_keep ) ดูpostgresql.org/docs/9.4/static/queries-union.html
Ufos

1

ฉันเจอคำถามนี้เพราะฉันมีปัญหาที่คล้ายกัน ฉันกำลังล้างฐานข้อมูลที่มีแถว 300M + ฐานข้อมูลสุดท้ายจะมีประมาณ 30% ของข้อมูลต้นฉบับเท่านั้น หากคุณกำลังเผชิญสถานการณ์ที่คล้ายกันจริง ๆ แล้วมันง่ายกว่าที่จะแทรกในตารางใหม่และจัดทำดัชนีใหม่แทนที่จะลบ

ทำอะไรที่ชอบ

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

ด้วยการจัดทำดัชนีที่เหมาะสมใน foo และแถบคุณสามารถหลีกเลี่ยงการสแกน Seq

จากนั้นคุณจะต้องสร้างดัชนีใหม่และเปลี่ยนชื่อตาราง

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