PlusMagi's Blog By Pitt Phunsanit MariaDB,MySql,WordPress,เทคโนโลยี เทคนิคแทรก Post ID ที่หายไปใน WordPress ด้วย CTE Recursive ในเสี้ยววินาที

เทคนิคแทรก Post ID ที่หายไปใน WordPress ด้วย CTE Recursive ในเสี้ยววินาที

สำหรับคนที่ดูแลฐานข้อมูล WordPress มาอย่างยาวนาน ปัญหาเรื่อง ID Gap หรือตัวเลข ID ในตาราง wp_posts กระโดดข้าม เป็นเรื่องที่พบได้บ่อยมาก ไม่ว่าจะเกิดจากการลบโพสต์ทิ้ง, การสร้าง auto-draft ซ้ำซ้อน, หรือการทดสอบระบบจนค่า AUTO_INCREMENT พุ่งนำหน้า ID จริงไปไกล

บทความนี้จะพามาดูเทคนิคการใช้ Common Table Expressions (CTE) แบบ Recursive เพื่อสร้างตัวเลขรันตั้งแต่ 1 จนถึงค่าก่อน Auto Increment และดึงเฉพาะช่องว่างที่ยังไม่มีข้อมูลมาทำ Placeholder Draft รวดเดียวเกือบ 12,000 แถว ในเวลาไม่ถึงครึ่งวินาที


ทำไมต้องเติม Gap ให้เต็ม?

  • จองสิทธิ์ ID ไว้ใช้งาน: บางระบบต้องการแมป URL แบบสั้น (เช่น /?p=123) ให้มีสถานะเป็น Draft ล่วงหน้า เพื่อป้องกันไม่ให้ระบบไปหยิบ ID ตัวเลขสวย ๆ ไปใช้กับ revision หรือ attachment อื่น
  • ความต่อเนื่องของข้อมูล: ลดความกระจัดกระจายของลำดับ ID ก่อนที่จะเริ่มรันข้อมูลชุดใหม่
  • ความเร็วระดับ Native SQL: การรันผ่าน PHP loop ทั่วไปเพื่อสร้าง 10,000+ โพสต์อาจใช้เวลานานและติด timeout แต่การใช้ CTE สามารถจบงานได้ในระดับ millisecond

โครงสร้างคำสั่ง SQL

หลักการทำงานคือการให้ CTE วนลูปสร้างตัวเลข n เริ่มต้นที่ 1 ไปจนถึง AUTO_INCREMENT - 1 จากนั้นใช้ LEFT JOIN ตรวจหาแถวที่ ID IS NULL เพื่อแทรกเฉพาะ ID ที่ยังว่างอยู่เท่านั้น

-- ขยายขีดจำกัดการวนซ้ำสำหรับ MariaDB
-- SET SESSION cte_max_recursion_depth = 100000; -- สำหรับ MySQL 8.0+
SET SESSION max_recursive_iterations = 100000;

INSERT INTO `wp_posts` (
  `ID`,
  `post_author`,
  `post_date`,
  `post_date_gmt`,
  `post_content`,
  `post_title`,
  `post_excerpt`,
  `post_status`,
  `comment_status`,
  `ping_status`,
  `post_password`,
  `post_name`,
  `to_ping`,
  `pinged`,
  `post_modified`,
  `post_modified_gmt`,
  `post_content_filtered`,
  `post_parent`,
  `guid`,
  `menu_order`,
  `post_type`,
  `post_mime_type`,
  `comment_count`
)
WITH RECURSIVE id_sequence (n, max_id) AS (
  SELECT
    1 AS n,
    (
      SELECT `AUTO_INCREMENT` - 1
      FROM information_schema.TABLES
      WHERE TABLE_SCHEMA = DATABASE()
        AND TABLE_NAME = 'wp_posts'
    ) AS max_id
  UNION ALL
  SELECT
    n + 1,
    max_id
  FROM id_sequence
  WHERE n < max_id
)
SELECT
  s.n,
  1,
  '1982-08-05 07:00:00',
  '1982-08-05 07:00:00',
  '',
  CAST(s.n AS CHAR),
  '',
  'draft',
  'closed',
  'closed',
  '',
  CAST(s.n AS CHAR),
  '',
  '',
  '1982-08-05 07:00:00',
  '1982-08-05 07:00:00',
  '',
  0,
  CONCAT('https://pitt.plusmagi.com/?p=', s.n),
  0,
  'post',
  '',
  0
FROM id_sequence AS s
LEFT JOIN `wp_posts` AS t ON s.n = t.ID
WHERE t.ID IS NULL;

ผลลัพธ์และประสิทธิภาพ

จากการทดสอบใช้งานจริงบนฐานข้อมูล MariaDB คำสั่งสามารถประมวลผลและแทรกแถวข้อมูลได้รวดเร็วอย่างไม่น่าเชื่อ

SELECT s.n AS missing_id
FROM (
  SELECT 1 AS n UNION ALL SELECT 2 -- หรือ query CTE สั้น ๆ
) s
LEFT JOIN `wp_posts` t ON s.n = t.ID
WHERE t.ID IS NULL;

11,917 rows inserted. (Query took 0.4613 seconds.)

การแทรกข้อมูลเกือบ 12,000 แถว ใช้เวลาไปเพียง 0.46 วินาที เท่านั้น โดยได้ผลลัพธ์เป็น Post Placeholder ที่มีสถานะ draft พร้อมใช้งาน โดยไม่รบกวนข้อมูลโพสต์เดิมที่มีอยู่ และยังคงรักษาเลข ID ตามลำดับที่ถูกต้องสมบูรณ์


อ่านเพิ่มเติม

Leave a Reply