【2026年10月21日時点の情報です】
このシリーズでは、第0回で作った練習用のWordPressで、AI(Claude)にお題を出してもらい、ヒントだけを見て自分でSQLを書いています。
第1回では、練習用のほうが公開中の記事が1本多く、その1本はWordPressに最初から入っていた「Hello world!」(ID 1)だと分かりました。第2回のお題は、この記事を練習用から消すことです。
本番でSQLを使って何かを消すときの、いちばん大事な流れの練習でもあります。消す前に控えを取り、消す行を確かめてから消し、消えたかを確かめます。
場面0:4つのテーブルのつながり
第1回で使ったのは、記事が入っている wp_posts だけでした。第2回では、記事を消したあとに、ほかのテーブルにも記事につながった行が残っていることが分かります。その前に、4つのテーブルと、そのつながりを整理しておきます。
| No. | テーブル | 中身 | 1行は | 記事を指している列 |
|---|---|---|---|---|
| 1 | wp_posts | 記事 | 記事1本 | ID(記事の番号そのもの) |
| 2 | wp_term_relationships | どの記事が、どのカテゴリーに入っているか | 記事とカテゴリーの組み合わせ1つ | object_id |
| 3 | wp_term_taxonomy | カテゴリー | カテゴリー1つ | ー(count に、そのカテゴリーの公開中の記事の数が入っている) |
| 4 | wp_comments | コメント | コメント1つ | comment_post_ID |

図の矢印は、「この列の数字は、矢印の先のテーブルの行を指している」という意味です。たとえば、wp_comments の comment_post_ID が 1 なら、「このコメントは、wp_posts の ID が 1 の記事に付いている」という意味になります。wp_term_relationships は、記事(object_id)とカテゴリー(term_taxonomy_id)の両方を指して、2つを結んでいます。
管理画面で記事を「完全に削除」すると、WordPress がこの矢印をたどって、つながった行も一緒に消します。SQL で wp_posts の行だけを消すと、矢印の元の行は残ったままになります。第2回は、まさにそれが起きる回です。
場面1:お題と、4つの段
お題は、4つの段に分けて出してもらいました。
| No. | 段 | やること |
|---|---|---|
| 1 | 控えを取る | Hello world! の記事の行だけを、ファイルに書き出す |
| 2 | 消す前に確かめる | 消す条件で、本当にその1行だけが選ばれるかを SELECT で見る |
| 3 | 消す | DELETE で消す |
| 4 | 消えたか確かめる | 同じ条件で、もう一度 SELECT する |
場面2:控えを取ったら、ちがう記事の控えだった
控えの取り方のヒントは、「mysqldump を使う」「sudo mysqldump データベース テーブル --where="条件" > 控えのファイル の順に並べる」でした。条件には、第1回で見つけた Hello world! の ID を入れます。書いたのは、次のコマンドです。
sudo mysqldump wp_practice wp_posts --where="ID IN (2)" > ~/work/wp_posts-backup-20261020.sql- mysqldump wp_practice wp_posts:
練習用のデータベースの、記事のテーブルを書き出す - –where=”ID IN (2)”:
条件に合う行だけを書き出す - > ~/work/wp_posts-backup-20261020.sql:
書き出したものを、このファイルに入れる
控えに Hello world! が入っているかを、grep で確かめようとしました。
grep -c "Hello world" wp_posts-backup-20261020.sql結果の例:
grep: wp_posts-backup-20261020.sql: そのようなファイルやディレクトリはありませんこのとき、いたのはホーム(~)でした。控えは ~/work/ の中に書き出していたので、見つからなかったのです。work の中に移ってから、もう一度流しました。
結果の例:
0今度はファイルは見つかりましたが、0 でした。控えに Hello world! が入っていません。第1回の結果を見直すと、Hello world! の ID は 1 でした。2 は別の行です。
| No. | まちがえたところ | 直し方 |
|---|---|---|
| 1 | grep のファイルの場所 | 書き出した先(~/work/)と同じ場所で探す |
| 2 | 控えの ID | 第1回で見た ID(1)にする |
ID を 1 にして書き出し直すと、grep の結果が 1 になりました。消す前に控えの中身を確かめたので、ここで気づけました。確かめずに進んでいたら、「控えはあるのに、消した記事の控えではなかった」ことになっていました。
ちなみに、ID IN (2) のように1つだけでも IN は動きます。1つだけのときは、ID = 1 と書くこともできます。
場面3:消す前に、消す条件で SELECT した
DELETE は、WHERE の条件に合う行を全部消します。条件をまちがえると、別の行まで消えてしまいます。そこで、消す前に同じ条件で SELECT して、選ばれる行を目で確かめました。
sudo mysql wp_practice -e "SELECT ID, post_title FROM wp_posts WHERE post_type='post' AND ID='1'";結果の例:
+----+--------------+
| ID | post_title |
+----+--------------+
| 1 | Hello world! |
+----+--------------+選ばれるのは、Hello world! の1行だけでした。
この SQL には、小さなことが2つありました。どちらも、結果には影響しません。
- ID=’1′ の引用符:
ID は数の列なので、ID=1と引用符なしで書くのがふつう。引用符を付けても、MariaDB が数に直してくれる - 最後の ;:
引用符の外の ; は、シェルの区切りの印として読まれる。SQLの終わりの ; にしたいときは、引用符の中に入れる
場面4:消して、消えたかを確かめた
場面3の SQL の、SELECT ID, post_title の部分だけを、消す命令に変えました。WHERE の条件は、場面3とまったく同じにします。
sudo mysql wp_practice -e "DELETE FROM wp_posts WHERE post_type='post' AND ID=1;"
sudo mysql wp_practice -e "SELECT ID, post_title FROM wp_posts WHERE post_type='post' AND ID=1;"- DELETE FROM wp_posts WHERE …:
条件に合う行を、記事のテーブルから消す。うまくいっても何も表示されない - 2行目の SELECT:
消したあとに、同じ条件で探す
2行目の SELECT は、何も出ませんでした。Hello world! の行は、もうありません。
場面5:消したのに、コメントとカテゴリーの行が残っていた
ここで、もう1つ考えるお題が出ました。管理画面で記事を「完全に削除」すると、その記事に付いていたものも一緒に消えます。でも、今回は SQL で wp_posts の1行だけを消しました。Hello world! の記事には、ほかに何が付いていたでしょうか。
ヒントは、SQLで下書きに戻したら空のカテゴリーが残った記事でした。答えは、カテゴリーです。どの記事がどのカテゴリーに入っているかは、wp_posts ではなく、別のテーブルに入っています。もう1つ、WordPressはインストールしたときに、Hello world! にコメントを1つ付けています。
| No. | 探すもの | テーブル | 記事の ID が入っている列 |
|---|---|---|---|
| 1 | カテゴリーの行 | wp_term_relationships | object_id |
| 2 | コメント | wp_comments | comment_post_ID |
最初に書いた SQL は、エラーになりました。
sudo mysql wp_practice -e "SELECT * FROM wp_posts WHERE object_id=1;"結果の例:
ERROR 1054 (42S22) at line 1: Unknown column 'object_id' in 'WHERE'- ERROR 1054:
「列が見つからない」という種類のエラー - Unknown column ‘object_id’ in ‘WHERE’:
WHERE に書いた object_id という列は知らない
列の名前は合っていましたが、テーブルがちがいました。object_id は、wp_posts ではなく wp_term_relationships の列です。テーブルごとに、持っている列がちがいます。どのテーブルにどんな列があるかは、DESCRIBE テーブル; で一覧にできます。
テーブルを直すと、残っている行が見つかりました。
sudo mysql wp_practice -e "SELECT * FROM wp_term_relationships WHERE object_id=1;"結果の例:
+-----------+------------------+------------+
| object_id | term_taxonomy_id | term_order |
+-----------+------------------+------------+
| 1 | 1 | 0 |
+-----------+------------------+------------+- object_id:
記事の ID(消した Hello world!) - term_taxonomy_id:
カテゴリーの番号。1 は、WordPressが最初から作る「未分類」
コメントも、同じ形で探しました。
sudo mysql wp_practice -e "SELECT * FROM wp_comments WHERE comment_post_ID=1;"こちらは列が多く、結果の表がとても横に長くなりました。見たい列だけを書き出すと、次のとおりです。
| No. | 列 | 値 |
|---|---|---|
| 1 | comment_ID | 1 |
| 2 | comment_post_ID | 1(消した Hello world!) |
| 3 | comment_author | WordPress コメントの投稿者 |
| 4 | comment_approved | 1(承認済み) |
SELECT * は全部の列を出すので、列が多いテーブルでは、SELECT comment_ID, comment_post_ID, comment_author FROM … のように見たい列だけを書くか、最後の ; を \G に変えて縦に並べると読みやすくなります。
場面6:控えから戻そうとしたら、控えが空になっていた
あとでもう一度練習できるように、控えから Hello world! を戻すことにしました。
控えには、テーブルを消す命令が入っていた
戻す前に教えてもらって、いちばん驚いたことがあります。mysqldump は、何も指定しないと、控えのファイルの中に「テーブルを消してから作り直す」命令も一緒に書き出します。
| No. | 控えのファイルの中身 | そのまま流すと |
|---|---|---|
| 1 | DROP TABLE IF EXISTS wp_posts; | wp_posts のテーブルを、まるごと消す |
| 2 | CREATE TABLE wp_posts (…); | 空の wp_posts を作り直す |
| 3 | INSERT INTO wp_posts VALUES (…); | 控えに入っている行だけを入れる |
つまり、1行だけの控えをそのまま mysql に流し込むと、ほかの記事が全部消えて、控えの1行だけのテーブルになってしまいます。本当に入っているかを、grep で確かめました。
grep -c "DROP TABLE" work/wp_posts-backup-20261020.sql結果の例:
1入っていました。そこで、ファイルをまるごと流すのではなく、INSERT の行だけを取り出して流すことにしました。
流す前の数を数えたら、またまちがえた
あとで「1本増えたか」を確かめるために、流す前の公開中の記事の数を数えました。ところが、最初に流したのは、第1回でまちがえたほうの SQL でした。
sudo mysql wp_practice -e "SELECT post_type, post_status, COUNT(*) AS n FROM wp_posts ORDER BY post_type='post', post_status='publish';"結果の例:
+------------+-------------+------+
| post_type | post_status | n |
+------------+-------------+------+
| attachment | inherit | 1039 |
+------------+-------------+------+ORDER BY ではしぼれないので、テーブルの全部の行を数えています。第1回では 1040 だったので、1行消えたことはここでも分かりますが、出したかったのは公開中の記事の数です。WHERE でしぼる形で数え直しました。
sudo mysql wp_practice -e "SELECT COUNT(*) FROM wp_posts WHERE post_type='post' AND post_status='publish'"結果の例:
+----------+
| COUNT(*) |
+----------+
| 172 |
+----------+消す前の 173 から1つ減って、本番と同じ 172 になっていました。
INSERT の行が、1行もなかった
流す前に、取り出す INSERT の行が1行だけかを数えました。
grep -c "^INSERT INTO" work/wp_posts-backup-20261020.sql- ^INSERT INTO:
行の頭が「INSERT INTO」で始まる行だけに合わせる。^ は「行の頭」の印
結果の例:
00 でした。おかしいので、流さずに、ファイルの中身を調べました。
grep -n "INSERT" work/wp_posts-backup-20261020.sql | cut -c1-80
grep -c "Hello world" work/wp_posts-backup-20261020.sql
ls -l work/wp_posts-backup-20261020.sql結果の例:
0
-rw-r--r-- 1 user user 3299 10月 21 10:03 work/wp_posts-backup-20261020.sql- 1つ目(INSERT を含む行):
何も出ない - 2つ目(Hello world):
0。場面2で確かめたときは 1 だった - 3つ目(ファイル):
最後に書き込まれたのは、控えを取った次の日の 10:03
控えのファイルが、上書きされていました。Hello world! を消したあとに、同じ名前で控えを取るコマンドがもう一度流れてしまい、行のない控え(テーブルの作り方だけ)になったのだと思います。上矢印のキーで前のコマンドを呼び出して、うっかり Enter を押すと、こうなりやすいです。
この控えからは、Hello world! は戻せません。ただ、流す前に grep -c で 0 と分かったので、空の控えを流さずに済みました。
管理画面で作り直して、上書きされない控えを取った
INSERT で ID 1 の行を自分で作り直す方法もありますが、wp_posts は列が多く、初心者には大変だったので、管理画面で「Hello world!」の記事を新しく作りました。ID を調べると、1 ではなく 4321 になっていました。本番から読み込んだ記事や画像で、もうたくさんの番号が使われているためです。
sudo mysql wp_practice -e "SELECT ID, post_title FROM wp_posts WHERE post_type='post' AND post_status='publish' AND post_name LIKE '%hello-world%'"結果の例:
+------+--------------+
| ID | post_title |
+------+--------------+
| 4321 | Hello world! |
+------+--------------+このSQLも、最初は SELECT のあとを ID, post_title に直したときに、WHERE の最後の条件(post_name LIKE …)を消してしまい、公開中の記事が全部出てきました。条件を減らすと、しぼり方がゆるくなって、出てくる行が増えます。
公開中の記事の数も、173 に戻りました。
今度は、上書きされず、そのまま流しても大丈夫な控えを取りました。
sudo mysqldump --no-create-info wp_practice wp_posts --where="ID = 4321" > ~/work/wp_posts-hello-$(date +%Y%m%d-%H%M%S).sql- –no-create-info:
テーブルを消して作り直す命令(DROP TABLE・CREATE TABLE)を書き出さない。INSERT の行だけになる - $(date +%Y%m%d-%H%M%S):
ファイルの名前に、今の日付と時刻を入れる。同じコマンドをもう一度流しても別の名前になるので、前の控えが上書きされない
できたファイル(wp_posts-hello-20261021-102819.sql)を確かめました。
| No. | 確かめたこと | 結果 |
|---|---|---|
| 1 | grep -c “Hello world” | 1(入っている) |
| 2 | grep -c “^INSERT INTO” | 1(INSERT の行がある) |
| 3 | grep -c “DROP TABLE” | 0(テーブルを消す命令はない) |
これで、次に消す練習をしたあとも、この控えから戻せます。
まとめ
第2回では、控えを取ってから記事を1本消し、控えから戻そうとして、戻せないことに気づきました。
| No. | テーブル | Hello world! の行 |
|---|---|---|
| 1 | wp_posts(記事) | 消した |
| 2 | wp_term_relationships(どのカテゴリーに入っているか) | 残っている |
| 3 | wp_comments(コメント) | 残っている |
| 4 | wp_term_taxonomy(カテゴリーの記事の数) | 数え直されていない |
管理画面で記事を消すと、WordPressがこの4つをまとめて片付けます。SQLで wp_posts だけを消すと、残りの3つは「もうない記事を指している行」として残ります。
覚えたことは、次のとおりです。
| No. | 覚えたこと | まちがえたところ |
|---|---|---|
| 1 | 控えを取ったら、中身に消す行が入っているかを確かめる | 控えの ID を 2 にしていた |
| 2 | ファイルを探すときは、書き出した場所で探す | ホームで探していた |
| 3 | DELETE の前に、同じ条件で SELECT する | ー(できた) |
| 4 | テーブルごとに、持っている列がちがう | object_id を wp_posts で探した |
| 5 | mysqldump の控えには、何も付けないとテーブルを消す命令が入る。1行だけ戻す控えは –no-create-info で取る | ー(戻す前に教えてもらった) |
| 6 | 控えの名前に時刻を入れて、上書きされないようにする。戻す前にも中身を確かめる | 同じ名前で取り直して、控えが空になった |
次の第3回は、残った行を片付けて、未分類の記事の数を数え直すお題です。
SQLの部品の意味は、データベースのコマンドの記事でも説明しています。



コメント