ข้ามไปยังเนื้อหา

การ update และ delete

สองคำกริยา CRUD สุดท้ายเปลี่ยนแปลงข้อมูลที่มีอยู่ UPDATE ตั้งค่า column ใหม่บน row ที่ตรงกับเงื่อนไข และ DELETE ลบ row ที่ตรงกัน ทั้งคู่พึ่งพา WHERE clause ที่คุณเรียนมาสำหรับ SELECT อย่างมาก — และทั้งคู่กลายเป็นความผิดพลาดที่สร้างความเสียหายหากคุณลืมใส่

เราใช้ table authors และ books กันต่อไป

UPDATE ระบุชื่อ table แสดงรายการคู่ SET column = value และใช้ WHERE เพื่อเลือก row หากไม่มี WHERE ทุก row จะถูกเปลี่ยน

UPDATE books
SET copies_sold = copies_sold + 500
WHERE id = 1;

ค่าใหม่สามารถอ้างถึงค่าเก่าได้ ดังที่ copies_sold + 500 แสดงให้เห็น คุณสามารถตั้งค่าหลาย column พร้อมกันได้โดยคั่นการกำหนดค่าแต่ละอันด้วยเครื่องหมายจุลภาค

ใน pgAdmin: รันสิ่งนี้ใน Query Tool แผงข้อความจะรายงานว่า UPDATE 1 ที่เป็นจำนวน row ที่ถูกเปลี่ยน

เช่นเดียวกับ INSERT ทั้ง UPDATE และ DELETE รองรับ RETURNING นี่เป็นวิธีที่สะอาดที่สุดในการดูว่า row ใดถูกเปลี่ยนแปลงเป๊ะ ๆ และค่าใหม่เป็นอะไร

UPDATE books
SET published = 2005
WHERE title = 'The Glass Orchard'
RETURNING id, title, published;
id | title | published
----+-------------------+-----------
1 | The Glass Orchard | 2005
(1 row)

DELETE FROM ลบ row ที่ตรงกับ WHERE clause เช่นเดียวกับ UPDATE คุณสามารถเพิ่ม RETURNING เพื่อดูว่าอะไรถูกลบไป

DELETE FROM books
WHERE copies_sold = 0
RETURNING id, title;
id | title
----+----------------
3 | Quiet Harbors
(1 row)

UPDATE และ DELETE ทำกับทุก row ที่ตรงกับเงื่อนไข — และถ้าไม่มีเงื่อนไข ทุก row ก็ตรงทั้งหมด การรัน DELETE FROM books; จะล้าง table ทั้งหมดให้ว่างเปล่า และ UPDATE books SET copies_sold = 0; จะทำให้ทุกระเบียนเป็นศูนย์ ไม่มีปุ่ม undo

นิสัยที่ปลอดภัยคือการรัน WHERE เดียวกันเป็น SELECT ก่อน ยืนยันจำนวน row แล้วค่อยสลับคำกริยา การห่อการเปลี่ยนแปลงที่เสี่ยงไว้ใน transaction จะให้ทางหนีฉุกเฉินแก่คุณ

BEGIN;
DELETE FROM books WHERE author_id = 4;
-- inspect the result, then decide:
ROLLBACK; -- or COMMIT; to keep the change

ภายใน transaction ไม่มีอะไรถาวรจนกว่าคุณจะ COMMIT ส่วน ROLLBACK จะทิ้งการเปลี่ยนแปลงไป transaction มีบทเรียนเต็ม ๆ ของตัวเองอยู่ช่วงหลังของคอร์ส

  • ใส่ WHERE clause บน UPDATE และ DELETE เสมอ เว้นแต่คุณตั้งใจจริง ๆ ที่จะแตะต้องทุก row การลืม WHERE จะเขียนทับหรือล้าง table ทั้งหมดให้ว่างเปล่า
  • แสดงตัวอย่างคำสั่งที่สร้างความเสียหายล่วงหน้าโดยการรัน SELECT count(*) ... WHERE ... ที่ตรงกันก่อน เพื่อยืนยันว่าคุณกำลังจะกระทบกี่ row
  • ห่อการเปลี่ยนแปลงที่เสี่ยงหรือหลายขั้นตอนไว้ใน transaction เพื่อให้คุณ ROLLBACK ได้ถ้าผลลัพธ์ผิด
  • ใช้ RETURNING เพื่อตรวจสอบว่า row ใดถูกเปลี่ยนแปลงเป๊ะ ๆ แทนการไว้ใจแค่จำนวน row เพียงอย่างเดียว
  • DELETE อาจล้มเหลวได้ถ้า table อื่นอ้างถึง row นั้นผ่าน foreign key คุณจะกลับมาเจอเรื่องนี้อีกในโมดูล join และความสัมพันธ์
ตัวเลือกBenefitCost
UPDATE/DELETE พร้อม WHERE ที่เจาะจงปลอดภัย กระทบเฉพาะ row ที่ตั้งใจต้องคิดเงื่อนไขให้ถูกต้องครบถ้วนก่อนรัน
UPDATE/DELETE โดยไม่มี WHEREเขียนเร็ว ไม่ต้องคิดเงื่อนไขกระทบทุก row ในทันที เป็นหายนะถ้ารันผิด table หรือผิด environment
ห่อไว้ใน transaction แล้ว ROLLBACK ได้มีทางหนีฉุกเฉินก่อน COMMIT จริงต้องจำเปิด/ปิด transaction และตรวจผลก่อน commit ทุกครั้ง
รันตรง ๆ นอก transactionเร็ว ไม่ต้องจัดการ transactionไม่มีปุ่ม undo ถ้าผลลัพธ์ผิดพลาด
  • รัน UPDATE/DELETE โดยไม่มี WHERE clause — กระทบทุก row ใน table ทันที ให้สร้างนิสัยพิมพ์ WHERE ก่อนแล้วค่อยเติมเงื่อนไข
  • ไม่ทดสอบเงื่อนไขด้วย SELECT ก่อน — ควรรัน SELECT count(*) ... WHERE ... ที่ตรงกันก่อนเสมอ เพื่อยืนยันจำนวน row ที่จะโดนผลกระทบ ก่อนสลับเป็น UPDATE/DELETE
  • คิดว่า DELETE คืนพื้นที่ดิสก์ทันทีDELETE แค่ทำเครื่องหมาย row ว่าตายแล้ว การคืนพื้นที่จริงเป็นหน้าที่ของ VACUUM

💡 ตัวอย่างจากของจริง

กระบวนการเปลี่ยนแปลงข้อมูล production ของ GitLab — กำหนดให้ dry-run คำสั่ง SQL ที่ทำลายข้อมูล (destructive) ด้วยเวอร์ชัน SELECT ก่อนเสมอ แล้วให้ทีมรีวิวจำนวน row ก่อนอนุมัติให้รัน UPDATE/DELETE จริง

ทีม SRE ทั่วไปที่ทำ manual data fix — บังคับให้ทุกการเปลี่ยนแปลงข้อมูลด้วยมือห่อด้วย transaction ที่ ROLLBACK ได้ พร้อมมีคนที่สองช่วยรีวิวผลก่อน COMMIT

จะเกิดอะไรขึ้นถ้ารัน UPDATE books SET copies_sold = 0; โดยไม่มี WHERE clause?
clause ตัวไหนทำให้ UPDATE หรือ DELETE รายงานได้ว่าเปลี่ยน row ไหนไปบ้าง?
จะทำให้ DELETE ที่เสี่ยงสามารถย้อนกลับได้ระหว่างที่ยังตรวจผลอยู่ได้อย่างไร?