前提・実現したいこと
MySQLで以下のタグテーブルwp_tags
があるとします。
PHPでタグ名の配列$tags = ['a','b'];
を受けて、
・なければ保存しそのIDを取得
・あればそのIDを取得
したいです。
###発生している問題
そのために複数のタグをSELECT
で存在確認し、なければINSERT
という流れを組んでみました。
しかし存在確認でIN
を使うと、インデックスが効きませんでした。
該当のソースコード
以下のタグテーブルとします。tag_name
にユニークがかかっている状態で、これを複数検索したいということです。
show crate table wp_tags
の結果↓
SQL
1CREATE TABLE `wp_tags` ( 2 `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 3 `tag_name` varchar(50) NOT NULL, 4 `lang_id` tinyint(2) unsigned NOT NULL DEFAULT 1, 5 `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), 6 `is_official` tinyint(1) unsigned NOT NULL DEFAULT '0', 7 PRIMARY KEY (`ID`), 8 UNIQUE KEY `u_tag_name` (`tag_name`), 9 KEY `i_is_official` (`is_official`), 10) ENGINE=InnoDB AUTO_INCREMENT=1334 DEFAULT CHARSET=utf8
上のテーブルはユニークなので次のクエリでtype=const
となると思ったのですが、EXPLAIN
をつけてみたら画像のようにtype=range
でして、ユニークが活躍できていません
EXPLAIN SELECT * FROM wp_tags WHERE tag_name IN ( 'a','b' );
###試したこと
まずINを一つにしたらtype=const
となりまして、複数ある場合の問題だと思います。
EXPLAIN SELECT * FROM wp_tags WHERE tag_name IN ( 'a' );
###考えた解決策
上記試したことから、要はIN
で複数検索することは非効率なのだと思いました。
そこで考えた解決策ですが、次の【A】【B】はいずれがよろしいでしょうか?
【A】
PHP側のforeach
を使い、一件ずつ存在確認をする
php
1$IDs = []; // $queryによって存在を確認し、存在するタグ名のIDを取得して入れる 2$tag_names = ['a','b']; // POSTされたタグ名で、存在すれば$IDsにいれ、存在しなければINSERTする 3$not_exist_tag_names = []; // 存在しないタグ名で、INSERTのときに使う 4 5// 存在を確認 6foreach($tag_names as $tag_name){ 7 $query = "SELECT * FROM wp_tags WHERE tag_name = $tag_name;"; 8} 9 10// bがなかったのでINSERTする 11$query = "INSERT INTO wp_tags (`tag_name`) VALUES ('b');"; 12 13// こんな感じの戻り値を予定(bだけ存在しなかったのでINSERTされ、aは存在したのでIDが取得された) 14return [ 15["status"=>"exist", "ID"=>1, "tag_name"=>"a"] 16["status"=>"insert", "ID"=>2, "tag_name"=>"b"] 17] 18];
【B】MySQL側のUNION
を使う
php
1$query = " 2EXPLAIN 3(SELECT * FROM wp_tags WHERE tag_name = 'a') 4UNION 5(SELECT * FROM wp_tags WHERE tag_name = 'b') 6";
または他に良い方法ございましたらご教示願えませんでしょうか。
どうぞ宜しくお願い致します。
###バージョン
MySQL 5.7.31
回答2件
あなたの回答
tips
プレビュー