[PR] 本記事にはプロモーションが含まれています。

【MariaDB】初心者のSQL練習帳 第2回:控えを取ってから記事を消した|消したのにコメントとカテゴリーが残っていた

記事を消したあとに、つながっていたコメントとカテゴリーの札が残っている様子を表したイラスト MySQL

【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行は記事を指している列
1wp_posts記事記事1本ID(記事の番号そのもの)
2wp_term_relationshipsどの記事が、どのカテゴリーに入っているか記事とカテゴリーの組み合わせ1つobject_id
3wp_term_taxonomyカテゴリーカテゴリー1つー(count に、そのカテゴリーの公開中の記事の数が入っている)
4wp_commentsコメントコメント1つcomment_post_ID
wp_posts、wp_term_relationships、wp_term_taxonomy、wp_commentsの4つのテーブルと、どの列がどのテーブルの行を指しているかを矢印で表した図

図の矢印は、「この列の数字は、矢印の先のテーブルの行を指している」という意味です。たとえば、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 を入れます。書いたのは、次のコマンドです。

Bash
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 で確かめようとしました。

Bash
grep -c "Hello world" wp_posts-backup-20261020.sql

結果の例:

Plaintext
grep: wp_posts-backup-20261020.sql: そのようなファイルやディレクトリはありません

このとき、いたのはホーム(~)でした。控えは ~/work/ の中に書き出していたので、見つからなかったのです。work の中に移ってから、もう一度流しました。

結果の例:

Plaintext
0

今度はファイルは見つかりましたが、0 でした。控えに Hello world! が入っていません。第1回の結果を見直すと、Hello world! の ID は 1 でした。2 は別の行です。

No.まちがえたところ直し方
1grep のファイルの場所書き出した先(~/work/)と同じ場所で探す
2控えの ID第1回で見た ID(1)にする

ID を 1 にして書き出し直すと、grep の結果が 1 になりました。消す前に控えの中身を確かめたので、ここで気づけました。確かめずに進んでいたら、「控えはあるのに、消した記事の控えではなかった」ことになっていました。

ちなみに、ID IN (2) のように1つだけでも IN は動きます。1つだけのときは、ID = 1 と書くこともできます。

場面3:消す前に、消す条件で SELECT した

DELETE は、WHERE の条件に合う行を全部消します。条件をまちがえると、別の行まで消えてしまいます。そこで、消す前に同じ条件で SELECT して、選ばれる行を目で確かめました。

Bash
sudo mysql wp_practice -e "SELECT ID, post_title FROM wp_posts WHERE post_type='post' AND ID='1'";

結果の例:

Plaintext
+----+--------------+
| 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とまったく同じにします。

Bash
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_relationshipsobject_id
2コメントwp_commentscomment_post_ID

最初に書いた SQL は、エラーになりました。

Bash
sudo mysql wp_practice -e "SELECT * FROM wp_posts WHERE object_id=1;"

結果の例:

Plaintext
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 テーブル; で一覧にできます。

テーブルを直すと、残っている行が見つかりました。

Bash
sudo mysql wp_practice -e "SELECT * FROM wp_term_relationships WHERE object_id=1;"

結果の例:

Plaintext
+-----------+------------------+------------+
| object_id | term_taxonomy_id | term_order |
+-----------+------------------+------------+
|         1 |                1 |          0 |
+-----------+------------------+------------+
  • object_id:
    記事の ID(消した Hello world!)
  • term_taxonomy_id:
    カテゴリーの番号。1 は、WordPressが最初から作る「未分類」

コメントも、同じ形で探しました。

Bash
sudo mysql wp_practice -e "SELECT * FROM wp_comments WHERE comment_post_ID=1;"

こちらは列が多く、結果の表がとても横に長くなりました。見たい列だけを書き出すと、次のとおりです。

No.列値
1comment_ID1
2comment_post_ID1(消した Hello world!)
3comment_authorWordPress コメントの投稿者
4comment_approved1(承認済み)

SELECT * は全部の列を出すので、列が多いテーブルでは、SELECT comment_ID, comment_post_ID, comment_author FROM … のように見たい列だけを書くか、最後の ; を \G に変えて縦に並べると読みやすくなります。

場面6:控えから戻そうとしたら、控えが空になっていた

あとでもう一度練習できるように、控えから Hello world! を戻すことにしました。

控えには、テーブルを消す命令が入っていた

戻す前に教えてもらって、いちばん驚いたことがあります。mysqldump は、何も指定しないと、控えのファイルの中に「テーブルを消してから作り直す」命令も一緒に書き出します。

No.控えのファイルの中身そのまま流すと
1DROP TABLE IF EXISTS wp_posts;wp_posts のテーブルを、まるごと消す
2CREATE TABLE wp_posts (…);空の wp_posts を作り直す
3INSERT INTO wp_posts VALUES (…);控えに入っている行だけを入れる

つまり、1行だけの控えをそのまま mysql に流し込むと、ほかの記事が全部消えて、控えの1行だけのテーブルになってしまいます。本当に入っているかを、grep で確かめました。

Bash
grep -c "DROP TABLE" work/wp_posts-backup-20261020.sql

結果の例:

Plaintext
1

入っていました。そこで、ファイルをまるごと流すのではなく、INSERT の行だけを取り出して流すことにしました。

流す前の数を数えたら、またまちがえた

あとで「1本増えたか」を確かめるために、流す前の公開中の記事の数を数えました。ところが、最初に流したのは、第1回でまちがえたほうの SQL でした。

Bash
sudo mysql wp_practice -e "SELECT post_type, post_status, COUNT(*) AS n FROM wp_posts ORDER BY post_type='post', post_status='publish';"

結果の例:

Plaintext
+------------+-------------+------+
| post_type  | post_status | n    |
+------------+-------------+------+
| attachment | inherit     | 1039 |
+------------+-------------+------+

ORDER BY ではしぼれないので、テーブルの全部の行を数えています。第1回では 1040 だったので、1行消えたことはここでも分かりますが、出したかったのは公開中の記事の数です。WHERE でしぼる形で数え直しました。

Bash
sudo mysql wp_practice -e "SELECT COUNT(*) FROM wp_posts WHERE post_type='post' AND post_status='publish'"

結果の例:

Plaintext
+----------+
| COUNT(*) |
+----------+
|      172 |
+----------+

消す前の 173 から1つ減って、本番と同じ 172 になっていました。

INSERT の行が、1行もなかった

流す前に、取り出す INSERT の行が1行だけかを数えました。

Bash
grep -c "^INSERT INTO" work/wp_posts-backup-20261020.sql
  • ^INSERT INTO:
    行の頭が「INSERT INTO」で始まる行だけに合わせる。^ は「行の頭」の印

結果の例:

Plaintext
0

0 でした。おかしいので、流さずに、ファイルの中身を調べました。

Bash
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

結果の例:

Plaintext
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 になっていました。本番から読み込んだ記事や画像で、もうたくさんの番号が使われているためです。

Bash
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%'"

結果の例:

Plaintext
+------+--------------+
| ID   | post_title   |
+------+--------------+
| 4321 | Hello world! |
+------+--------------+

このSQLも、最初は SELECT のあとを ID, post_title に直したときに、WHERE の最後の条件(post_name LIKE …)を消してしまい、公開中の記事が全部出てきました。条件を減らすと、しぼり方がゆるくなって、出てくる行が増えます。

公開中の記事の数も、173 に戻りました。

今度は、上書きされず、そのまま流しても大丈夫な控えを取りました。

Bash
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.確かめたこと結果
1grep -c “Hello world”1(入っている)
2grep -c “^INSERT INTO”1(INSERT の行がある)
3grep -c “DROP TABLE”0(テーブルを消す命令はない)

これで、次に消す練習をしたあとも、この控えから戻せます。

まとめ

第2回では、控えを取ってから記事を1本消し、控えから戻そうとして、戻せないことに気づきました。

No.テーブルHello world! の行
1wp_posts(記事)消した
2wp_term_relationships(どのカテゴリーに入っているか)残っている
3wp_comments(コメント)残っている
4wp_term_taxonomy(カテゴリーの記事の数)数え直されていない

管理画面で記事を消すと、WordPressがこの4つをまとめて片付けます。SQLで wp_posts だけを消すと、残りの3つは「もうない記事を指している行」として残ります。

覚えたことは、次のとおりです。

No.覚えたことまちがえたところ
1控えを取ったら、中身に消す行が入っているかを確かめる控えの ID を 2 にしていた
2ファイルを探すときは、書き出した場所で探すホームで探していた
3DELETE の前に、同じ条件で SELECT するー(できた)
4テーブルごとに、持っている列がちがうobject_id を wp_posts で探した
5mysqldump の控えには、何も付けないとテーブルを消す命令が入る。1行だけ戻す控えは –no-create-info で取るー(戻す前に教えてもらった)
6控えの名前に時刻を入れて、上書きされないようにする。戻す前にも中身を確かめる同じ名前で取り直して、控えが空になった

次の第3回は、残った行を片付けて、未分類の記事の数を数え直すお題です。

SQLの部品の意味は、データベースのコマンドの記事でも説明しています。

ほかの開発・プログラミングの記事は、開発・プログラミングメモの目次から、テーマ別に探せます。

このブログは、にほんブログ村と人気ブログランキングに参加しています。記事が役に立ったら、下のランキングのバナーのどれかを押して応援してもらえると励みになります。

ブログランキング・にほんブログ村へにほんブログ村

サーバー構築・運用ランキング プログラムランキング

MySQL
watasoftdevをフォローする

コメント