【2026年10月21日時点の情報です】
このシリーズでは、練習用のWordPressで、AI(Claude)にお題を出してもらい、ヒントだけを見て自分でSQLを書いています。
第2回では、SQLで Hello world!(ID 1)の記事を消したところ、その記事を指したままのカテゴリーの行とコメントの行が残っていました。第3回のお題は、この残りを片付けることです。
流れは第2回と同じ「控えを取る → 確かめる → 消す → 確かめる」ですが、今回はテーブルが2つあります。その分、前のコマンドをまねしたときの直し忘れが、たくさん出ました。
場面0:お題の前に知っておくこと
片付ける2つの行
第2回の場面0で整理した4つのテーブルのうち、今回さわるのは次の3つです。
| No. | テーブル | 残っている行 | 消した記事(ID 1)を指している列 |
|---|---|---|---|
| 1 | wp_term_relationships | 「ID 1 の記事は、未分類に入っている」という行 | object_id |
| 2 | wp_comments | ID 1 の記事に付いていたコメントの行 | comment_post_ID |
| 3 | wp_term_taxonomy | (消さない)未分類の記事の数(count)が正しいかを見る | ー |
テーブルごとに、記事を指している列の名前がちがいます。wp_posts では ID でしたが、ほかのテーブルには ID という列はありません。
なお、第2回のあとで、管理画面から新しい Hello world! を作り直しています(ID 4321)。今回消すのは ID 1 を指している行だけで、4321 の行は残します。
控えは、テーブルを消す命令を入れずに取る
第2回で分かったとおり、mysqldump で何も付けずに控えを取ると、テーブルを消して作り直す命令(DROP TABLE)も入ります。今回は最初から、次の2つを入れた形で取ります。
--no-create-info:
DROP TABLE・CREATE TABLE を書き出さない。INSERT の行だけになる$(date +%Y%m%d-%H%M%S):
ファイルの名前に日付と時刻を入れて、上書きされないようにする
SELECT と DELETE の形のちがい
| No. | 命令 | 形 | 列を書くか |
|---|---|---|---|
| 1 | SELECT | SELECT 列 FROM テーブル WHERE 条件 | 書く(どの列を見るか) |
| 2 | DELETE | DELETE FROM テーブル WHERE 条件 | 書かない(行をまるごと消す) |
count は、公開中の記事だけを数える
wp_term_taxonomy の count には、そのカテゴリーに入っている記事のうち、公開中(publish)の記事の数が入っています。下書きや非公開の記事は、カテゴリーに入っていても数えません。
管理画面で記事を作ったり消したりすると、WordPress が count を数え直します。SQL で行を消したときは、数え直しません(SQLで下書きに戻したら空のカテゴリーが残った記事と同じ理由です)。
場面1:控えを取ったら、中身が別のテーブルだった
最初のお題は、残っている2つの行の控えを取ることでした。第2回の最後に取った、新しい Hello world! の控えのコマンドをもとにして、ファイルの名前を2つに分けて書きました。
sudo mysqldump --no-create-info wp_practice wp_posts --where="ID = 4321" > ~/work/wp_term_relationships-hello-$(date +%Y%m%d-%H%M%S).sql
sudo mysqldump --no-create-info wp_practice wp_posts --where="ID = 4321" > ~/work/wp_comments-hello-$(date +%Y%m%d-%H%M%S).sql控えに INSERT の行があるか、テーブルを消す命令がないかを、grep で確かめました。
| No. | ファイル | grep -c “^INSERT INTO” | grep -c “DROP TABLE” |
|---|---|---|---|
| 1 | wp_term_relationships-hello-20261021-104834.sql | 1 | 0 |
| 2 | wp_comments-hello-20261021-104834.sql | 1 | 0 |
どちらも思ったとおりの数でしたが、中身はまちがっていました。もとにしたコマンドの、ファイルの名前だけを変えていて、テーブル(wp_posts)と条件(ID = 4321)がそのままでした。2つとも、中身は新しい Hello world! の記事の行です。
| No. | 書いたところ | 書いたもの | 本当は |
|---|---|---|---|
| 1 | テーブル | wp_posts(2回とも) | wp_term_relationships と wp_comments |
| 2 | 条件 | ID = 4321(2回とも) | object_id = 1 と comment_post_ID = 1 |
| 3 | ファイルの名前 | wp_term_relationships-hello-…、wp_comments-hello-… | 合っていた |
grep -c "^INSERT INTO" は、INSERT の行があるかしか見ていません。どのテーブルの INSERT かまで確かめるには、INSERT の行に入っているテーブルの名前で探します。
grep -c 'INSERT INTO `wp_term_relationships`' wp_term_relationships-hello-20261021-104834.sql- ‘INSERT INTO `wp_term_relationships`’:
mysqldump は、テーブルの名前を ` `(バッククォート)で囲んで書き出す。シェルで ` を文字のまま探すために、全体を ‘ ‘ で囲む
場面2:テーブルを直したら、ID という列がなかった
テーブルの名前だけを直して、もう一度流しました。
sudo mysqldump --no-create-info wp_practice wp_term_relationships --where="ID = 4321" > ~/work/wp_term_relationships-hello-$(date +%Y%m%d-%H%M%S).sql
sudo mysqldump --no-create-info wp_practice wp_comments --where="ID = 4321" > ~/work/wp_comments-hello-$(date +%Y%m%d-%H%M%S).sql結果の例:
mysqldump: Couldn't execute 'SELECT /*!40001 SQL_NO_CACHE */ `object_id`, `term_taxonomy_id`, `term_order` FROM `wp_term_relationships` WHERE ID = 4321': Unknown column 'ID' in 'WHERE' (1054)
mysqldump: Couldn't execute 'SELECT /*!40001 SQL_NO_CACHE */ `comment_ID`, `comment_post_ID`, `comment_author`, `comment_author_email`, `comment_author_url`, `comment_author_IP`, `comment_date`, `comment_date_gmt`, `comment_content`, `comment_karma`, `comment_approved`, `comment_agent`, `comment_type`, `comment_parent`, `user_id` FROM `wp_comments` WHERE ID = 4321': Unknown column 'ID' in 'WHERE' (1054)- Unknown column ‘ID’ in ‘WHERE’ (1054):
WHERE に書いた ID という列が、このテーブルにない。第2回の場面5と同じエラー - SELECT のあとに並んでいる名前:
そのテーブルの列の名前。エラーの文の中に、列の一覧が出ている
条件の列の名前を、テーブルに合わせる必要がありました。それに、値も 4321 ではなく、片付けたいのは ID 1 を指している行です。
もう1つ、コマンドがエラーで止まっても、> の部分で、空のファイルが作られていました。> は、コマンドを動かす前に、書き出す先のファイルを作るためです。
場面3:ちがうファイルを確かめて、こんがらがった
次に流した2つのコマンドです。
sudo mysqldump --no-create-info wp_practice wp_comments --where="comment_post_ID = 1" > ~/work/wp_comments-hello-$(date +%Y%m%d-%H%M%S).sql
sudo mysqldump --no-create-info wp_practice wp_term_relationships --where="object_id = 1 = 1" > ~/work/wp_comments-hello-$(date +%Y%m%d-%H%M%S).sql1つ目(wp_comments)は正しく書けていました。2つ目には、まちがいが2つありました。
| No. | 書いたところ | 書いたもの | 本当は |
|---|---|---|---|
| 1 | 条件 | object_id = 1 = 1 | object_id = 1(「= 1」が1つ多い) |
| 2 | ファイルの名前 | wp_comments-hello-… | wp_term_relationships-hello-…(1つ目のコマンドをまねして、名前を直し忘れた) |
No.1 は、たまたまエラーにはなりません。MariaDB は「(object_id = 1) が 1(本当)」と読むので、結果は object_id = 1 と同じになります。
そのうえ、確かめの grep では、場面2のエラーのときにできた空のファイルを見ていたので、0 が出て、何が正しいのか分からなくなりました。この時点で、wp_comments-hello- と wp_term_relationships-hello- で始まるファイルが、7つもできていました。
いったん全部消して、完成したコマンドで取り直した
ここで、お題3で作ったファイルを全部消して、最初から取り直すことにしました。残すのは、第2回で取った新しい Hello world! の控え(wp_posts-hello-20261021-102819.sql)だけです。
cd ~/work
ls -l wp_comments-hello-*.sql wp_term_relationships-hello-*.sql
rm wp_comments-hello-*.sql wp_term_relationships-hello-*.sql- ls -l …:
消す前に、消えるファイルの一覧を見る - rm …:
名前が wp_comments-hello- と wp_term_relationships-hello- で始まるファイルを消す
取り直しは、こんがらがらないように、完成したコマンドを教えてもらいました。1つのコマンドの中で、テーブル・条件の列・ファイルの名前の3か所がそろっているかを見比べます。
sudo mysqldump --no-create-info wp_practice wp_term_relationships --where="object_id = 1" > ~/work/wp_term_relationships-hello-$(date +%Y%m%d-%H%M%S).sql
sudo mysqldump --no-create-info wp_practice wp_comments --where="comment_post_ID = 1" > ~/work/wp_comments-hello-$(date +%Y%m%d-%H%M%S).sql| No. | 見比べるところ | 1行目 | 2行目 |
|---|---|---|---|
| 1 | テーブル | wp_term_relationships | wp_comments |
| 2 | 条件の列 | object_id | comment_post_ID |
| 3 | ファイルの名前の頭 | wp_term_relationships-hello- | wp_comments-hello- |
2つのファイルの中身を、テーブルの名前まで確かめました。
ls -l ~/work/wp_*-hello-*.sql
for f in ~/work/wp_term_relationships-hello-*.sql ~/work/wp_comments-hello-*.sql; do echo "== $f"; grep -o 'INSERT INTO `[a-z_]*`' "$f"; grep -c "DROP TABLE" "$f"; done- grep -o ‘INSERT INTO `[a-z_]*`’ “$f”:
INSERT の行の、テーブルの名前の部分だけを出す - for f in …; do …; done:
2つのファイルを、1つずつ順番に調べる
結果の例:
-rw-r--r-- 1 user user 1938 10月 21 12:54 /home/user/work/wp_comments-hello-20261021-125438.sql
-rw-r--r-- 1 user user 1753 10月 21 10:28 /home/user/work/wp_posts-hello-20261021-102819.sql
-rw-r--r-- 1 user user 1515 10月 21 12:54 /home/user/work/wp_term_relationships-hello-20261021-125434.sql
== /home/user/work/wp_term_relationships-hello-20261021-125434.sql
INSERT INTO `wp_term_relationships`
0
== /home/user/work/wp_comments-hello-20261021-125438.sql
INSERT INTO `wp_comments`
0ファイルの名前と、INSERT INTO のあとのテーブルの名前が同じで、DROP TABLE が 0 になりました。今度こそ、正しい控えです。
場面4:消す前の SELECT で、また前の形をまねした
消す前に、控えと同じ条件で SELECT して、消す行を確かめます。最初に書いたのは、次の2つです。
sudo mysql wp_practice -e "SELECT comment_ID, comment_post_ID, comment_author FROM wp_comments WHERE comment_ID=1"
sudo mysql wp_practice -e "SELECT ID, post_title FROM wp_term_relationships WHERE object_id=1 AND term_taxonomy_id=1"結果の例:
+------------+-----------------+------------------------------------+
| comment_ID | comment_post_ID | comment_author |
+------------+-----------------+------------------------------------+
| 1 | 1 | WordPress コメントの投稿者 |
+------------+-----------------+------------------------------------+
ERROR 1054 (42S22) at line 1: Unknown column 'ID' in 'SELECT'2つ目は、SELECT のあとに、wp_posts のときの列(ID, post_title)を書いていました。エラーの場所が、今度は ‘WHERE’ ではなく ‘SELECT’ になっています。wp_term_relationships の列は3つしかないので、SELECT * にしました。
もう1つ、どちらも条件が、控えを取ったときとちがっていました。
| No. | テーブル | 控えの条件 | 書いた SELECT の条件 |
|---|---|---|---|
| 1 | wp_comments | comment_post_ID = 1 | comment_ID=1 |
| 2 | wp_term_relationships | object_id = 1 | object_id=1 AND term_taxonomy_id=1 |
今回は、たまたまどちらも同じ1行が出ますが、SELECT で確かめる意味は、「控えの行」「確かめた行」「消す行」が同じかを見ることです。3つの条件をそろえておかないと、ずれることがあります。たとえば、もし記事が2つのカテゴリーに入っていたら、AND で term_taxonomy_id までしぼった形では、1つしか選ばれません。
控えと同じ条件にそろえて、流し直しました。
sudo mysql wp_practice -e "SELECT * FROM wp_term_relationships WHERE object_id=1"
sudo mysql wp_practice -e "SELECT comment_ID, comment_post_ID, comment_author FROM wp_comments WHERE comment_post_ID=1"結果の例:
+-----------+------------------+------------+
| object_id | term_taxonomy_id | term_order |
+-----------+------------------+------------+
| 1 | 1 | 0 |
+-----------+------------------+------------+
+------------+-----------------+------------------------------------+
| comment_ID | comment_post_ID | comment_author |
+------------+-----------------+------------------------------------+
| 1 | 1 | WordPress コメントの投稿者 |
+------------+-----------------+------------------------------------+どちらも、消したい1行だけが選ばれました。
場面5:DELETE に列を書いて、エラーになった
ヒントは「SELECT … FROM の部分を DELETE FROM に変える」でした。私は、SELECT のところだけを DELETE に変えて、列や * を残したまま流しました。
sudo mysql wp_practice -e "DELETE comment_ID, comment_post_ID, comment_author FROM wp_comments WHERE comment_post_ID=1"
sudo mysql wp_practice -e "DELETE * FROM wp_term_relationships WHERE object_id=1"結果の例:
ERROR 1109 (42S02) at line 1: Unknown table 'comment_ID' in MULTI DELETE
ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '* FROM wp_term_relationships WHERE object_id=1' at line 1- ERROR 1109 … in MULTI DELETE:
DELETE のすぐあとに名前を書くと、MariaDB は「いくつかのテーブルから、まとめて消す」書き方(MULTI DELETE)だと読む。comment_ID というテーブルはないので、エラー - ERROR 1064 near ‘* FROM …’:
DELETE のあとに * は書けない、という文の形のエラー
どちらもエラーで止まったので、何も消えていません。場面0の表のとおり、DELETE には列を書きません。行をまるごと消すからです。
sudo mysql wp_practice -e "DELETE FROM wp_term_relationships WHERE object_id=1"
sudo mysql wp_practice -e "DELETE FROM wp_comments WHERE comment_post_ID=1"今度は何も表示されずに終わりました。DELETE は、うまくいっても何も出ません。
消えたかの確かめで、古い SELECT を呼び出した
消えたかを確かめるために、場面4の SELECT をもう一度流しました。wp_comments は何も出ず、消えていました。ところが wp_term_relationships のほうは、上矢印のキーで呼び出したコマンドが、場面4で最初にまちがえた SELECT(SELECT ID, post_title … AND term_taxonomy_id=1)で、また ERROR 1054 が出ました。第2回で控えが上書きされたのと同じ、「履歴から呼び出したコマンドが、思っていたものとちがう」まちがいです。
正しい SELECT で、確かめ直しました。
sudo mysql wp_practice -e "SELECT * FROM wp_term_relationships WHERE object_id=1"何も出ませんでした。ID 1 を指していた2つの行は、どちらも消えています。
場面6:未分類の数を確かめたら、もう正しかった
最後は、未分類の記事の数(count)です。SQL で未分類の行を消したので、count は前の数のままのはず、と考えていました。
最初は、count を wp_term_relationships で探して、エラーになりました。
sudo mysql wp_practice -e "SELECT term_taxonomy_id, count FROM wp_term_relationships WHERE term_taxonomy_id=1"結果の例:
ERROR 1054 (42S22) at line 1: Unknown column 'count' in 'SELECT'count があるのは、名前のよく似た wp_term_taxonomy のほうでした。
| No. | テーブル | 中身 | 1行は |
|---|---|---|---|
| 1 | wp_term_relationships | どの記事が、どのカテゴリーに入っているか | 記事とカテゴリーの組み合わせ1つ |
| 2 | wp_term_taxonomy | カテゴリーそのもの(count はここ) | カテゴリー1つ |
sudo mysql wp_practice -e "SELECT term_taxonomy_id, count FROM wp_term_taxonomy WHERE term_taxonomy_id=1"結果の例:
+------------------+-------+
| term_taxonomy_id | count |
+------------------+-------+
| 1 | 1 |
+------------------+-------+count は 1 でした。これが「消した Hello world! の分が残った 1」なのか、「新しい Hello world!(ID 4321)の 1」なのかを見るために、未分類に入っている記事を出しました。
sudo mysql wp_practice -e "SELECT * FROM wp_term_relationships WHERE term_taxonomy_id=1"30行が出ました。ID 4321 のほかに、本番から読み込んだ記事(下書きにした Laravel の記事などと同じ番号)が入っていました。count は公開中の記事だけを数えるので、30行のうち公開中がいくつかを数えます。ここは、2つのテーブルをつなぐ JOIN を使うので、完成した SQL を教えてもらいました。
sudo mysql wp_practice -e "SELECT p.post_status, COUNT(*) AS n FROM wp_term_relationships tr JOIN wp_posts p ON p.ID = tr.object_id WHERE tr.term_taxonomy_id = 1 AND p.post_type = 'post' GROUP BY p.post_status;"- wp_term_relationships tr:
テーブルに tr という短い呼び名を付ける - JOIN wp_posts p ON p.ID = tr.object_id:
wp_posts を p と呼んでつなげる。tr の object_id と、p の ID が同じ行どうしを、1つの行にする - GROUP BY p.post_status:
記事の状態(公開中・下書きなど)ごとにまとめて数える
結果の例:
+-------------+----+
| post_status | n |
+-------------+----+
| draft | 28 |
| private | 1 |
| publish | 1 |
+-------------+----+公開中は1本(ID 4321)だけでした。count の 1 は、正しい数です。新しい Hello world! を管理画面で作ったときに、WordPress が未分類の数を数え直していたので、今回は数え直す必要がありませんでした。
まとめ
第3回では、消した記事(ID 1)を指したまま残っていた2つの行を、控えを取ってから片付けました。未分類の数は、管理画面で記事を作ったときに数え直されていて、正しいままでした。
覚えたことは、次のとおりです。
| No. | 覚えたこと | まちがえたところ |
|---|---|---|
| 1 | 前のコマンドをまねするときは、テーブル・条件の列・ファイルの名前の3か所を見比べる | テーブルと条件が wp_posts・ID = 4321 のままだった、ファイルの名前を直し忘れた |
| 2 | どのテーブルの控えかは、INSERT INTO のあとのテーブルの名前で確かめる | INSERT の行があるかだけを見て、中身がちがうのに気づかなかった |
| 3 | テーブルごとに、記事を指している列の名前がちがう。エラーの文に列の一覧が出る | wp_term_relationships と wp_comments に ID を書いた |
| 4 | > は、コマンドがエラーでも先にファイルを作る。確かめるのは、今作ったファイル | エラーのときの空のファイルを grep していた |
| 5 | 控え・確かめる SELECT・DELETE の条件は、同じにそろえる | SELECT で別の条件を書いた |
| 6 | DELETE には列も * も書かない | DELETE のあとに列や * を残した |
| 7 | 履歴から呼び出したコマンドは、流す前に読み直す | 古い SELECT を呼び出して流した |
| 8 | count は wp_term_taxonomy にあり、公開中の記事だけを数える | 名前の似た wp_term_relationships で探した |
今回いちばん多かったのは、前のコマンドをまねして、どこかを直し忘れるまちがいでした。コピーして直すのは速いですが、直す場所が多いほど、直し忘れも増えます。
SQLで記事を触ると、カテゴリーの数がずれることは、SQLで下書きに戻したら空のカテゴリーが残った記事でも書いています。SQLの部品の意味は、データベースのコマンドの記事で説明しています。



コメント