###前提・実現したいこと
WordPressでON DUPLICATE KEY UPDATE
の結果、INSERT か UPDATE か判別
したいです。
そこで$wpdb
を使わず自前のクエリを書きたいのです。
###発生している問題
自前のクエリですと処理が異常に遅くなるので、これを解消したいです。
具体的には、jQueryはtimeout
しますし、実行から数分はphpMyAdminから当該テーブルwp_actions_test
へもアクセスできなくなります。
###テーブルのCREATE
SQL
1CREATE TABLE IF NOT EXISTS wp_actions_test ( 2 ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT 3,users_ID BIGINT(20) UNSIGNED NOT NULL 4,action_name_id TINYINT(3) UNSIGNED NOT NULL 5,content_id BIGINT(20) UNSIGNED NOT NULL 6,created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) 7,updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) 8,is_deleted TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 9,PRIMARY KEY (ID) 10,UNIQUE unique_wp_actions_test (users_ID,content_id,action_name_id) 11);
###該当のソースコード
jQuery
1function ajax_update_actions_test(){ 2 3 // wp_actions_testへINSERTする値 4 const actions = { 5 'users_ID' : 1, // 実行者 6 'action_name_id' : 10, // アクション種別 7 'content_id' : 100, // 対象コンテンツ 8 }; 9 10 $.ajax({ 11 url: "http://example.com/wp-admin/admin-ajax.php", 12 type: "POST", 13 dataType: "json", 14 timeout: 10000, 15 data: { 16 action: "my_ajax_update_actions_test", 17 actions: actions 18 } 19 }).done(function(response) { 20 console.log(response); 21 }).fail(function() { 22 console.log('error'); 23 }); 24}
php
1// ajax処理 2add_action( 'wp_ajax_my_ajax_update_actions_test', 'my_ajax_update_actions_test' ); 3function my_ajax_update_actions_test() { 4 global $wpdb; 5 6 $status = 'unexecuted'; 7 $result = ['status'=>$status]; 8 try { 9 $errors = []; 10 11 // JSから値を受け取る 12 $actions = $_POST['actions'] ? $_POST['actions'] : []; 13 14 // テーブルロック 15 $wpdb->query('SET autocommit=0'); 16 $sql = 'LOCK TABLES wp_actions_test WRITE'; 17 $locked = $wpdb->query($sql); 18 if($locked === false){ 19 $errors['locked'] = [ 20 'reason' => 'テーブルロックでエラー', 21 'wpdb_errors'=> my_get_wpdb_errors() 22 ]; 23 } 24 25 // テーブルロックが問題なければ 26 else{ 27 28 // wp_actions_testの更新 29 $inserted = insert_actions_test($actions); 30 if(isset($inserted['errors'])){ 31 $errors['inserted'] = [ 32 'reason' => 'wp_actions_testの更新でエラー', 33 'result' => $inserted 34 ]; 35 } 36 } 37 38 if( !empty($errors) ){ 39 throw $e; 40 }else{ 41 $result = [ 42 'status' => $inserted['status'], 43 'actions' => $inserted['actions'], 44 ]; 45 46 $wpdb->query('COMMIT'); 47 } 48 } catch (Exception $e) { 49 $wpdb->query('ROLLBACK'); 50 $result = [ 51 'status' => 'error', 52 'errors' => $errors 53 ]; 54 } finally { 55 $wpdb->query('UNLOCK TABLES'); 56 } 57 58 echo json_encode($result,JSON_UNESCAPED_UNICODE); 59 die(); 60} 61 62// アクションテーブル更新 63function insert_actions_test($actions){ 64 global $wpdb; 65 66 $status = 'unexecuted'; 67 $result = ['status'=>$status]; 68 try { 69 $errors = []; 70 71 /* 72 自前バージョン 73 -------------------------------------------*/ 74 // INSERTか、過去に実行済(is_deleted=1)なら is_deleted=0 に戻す 75 $keys = array_keys($actions); 76 $vals = array_values($actions); 77 $keys = implode( ',', $keys ); 78 $vals = implode( ',', array_map( function($v){ return '?'; }, $vals ) ); 79 80 // SQL文生成 81 $sql = " 82 INSERT INTO wp_actions_test ( {$keys} ) 83 VALUES ( {$vals} ) 84 ON DUPLICATE KEY UPDATE is_deleted = 0;"; 85 86 // クエリの実行 87 $stmt = ''; 88 $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME); 89 if(mysqli_connect_errno()){ 90 $errors['mysqli'] = [ 91 'reason' => '接続でエラー', 92 'mysqli_connect_error' => mysqli_connect_error() 93 ]; 94 }else{ 95 if($stmt = $mysqli->prepare($sql)){ 96 $formats = implode('', array_map( function($v){ return is_numeric($v) ? 's' : 'i'; }, $vals ) ); 97 $stmt->bind_param($formats, ...$vals); 98 $stmt->execute(); 99 $res = $stmt->affected_rows; 100 $stmt->free_result(); 101 $stmt->close(); 102 103 // INSERT か UPDATE か判別 104 $status = 'undecided'; 105 if( $res === 0 ){ 106 $status = 'unchange'; 107 }elseif( $res === 1 ){ 108 $status = 'insert'; 109 }elseif( $res === 2 ){ 110 $status = 'update'; 111 } 112 } 113 } 114 115 if( !empty($errors) ){ 116 throw $e; 117 }else{ 118 $result = [ 119 'status' => $status, 120 'actions' => $actions, 121 ]; 122 } 123 } catch (Exception $e) { 124 $result = [ 125 'status' => 'error', 126 'errors' => $errors 127 ]; 128 } 129 130 return $result; 131}
###試したこと1
上記の自前バージョン
を、次のように$wpdbバージョン
とすると、処理はすぐに終わり問題解決しました。
しかしこれではINSERT か UPDATE か判別できない
ので困っています。
php
1// ajax処理 2add_action( 'wp_ajax_my_ajax_update_actions_test', 'my_ajax_update_actions_test' ); 3function my_ajax_update_actions_test() { 4 // 該当のソースコードと同じ 5} 6 7// アクションテーブル更新 8function insert_actions_test($actions){ 9 global $wpdb; 10 11 $status = 'unexecuted'; 12 $result = ['status'=>$status]; 13 try { 14 $errors = []; 15 16 /* 17 $wpdbバージョン 18 -------------------------------------------*/ 19 // INSERTか、過去に実行済(is_deleted=1)なら is_deleted=0 に戻す 20 $keys = array_keys($actions); 21 $vals = array_values($actions); 22 $keys = implode( ',', $keys ); 23 $vals = implode( ',', array_map( function($v){ 24 $type = gettype($v); 25 if($type == 'string'){ 26 return '%s'; 27 }elseif($type == 'integer'){ 28 return '%d'; 29 } 30 }, $vals ) ); 31 32 // SQL文生成 33 $sql = " 34 INSERT INTO wp_actions_test ( {$keys} ) 35 VALUES ( {$vals} ) 36 ON DUPLICATE KEY UPDATE is_deleted = 0;"; 37 38 // クエリの実行 39 $prepares = $vals; 40 $sql = $wpdb->prepare( $sql, ...$prepares ); 41 $rows = $wpdb->query( $sql, ARRAY_A ); 42 if($wpdb->last_error){ 43 $errors['rows'] = [ 44 'reason' => 'クエリの実行でエラー', 45 'wpdb_errors' => my_get_wpdb_errors() 46 ]; 47 }else{ 48 // INSERT か UPDATE か判別できない 49 $status = 'insert'; 50 } 51 52 if( !empty($errors) ){ 53 throw $e; 54 }else{ 55 $result = [ 56 'status' => $status, 57 'actions' => $actions, 58 ]; 59 } 60 } catch (Exception $e) { 61 $result = [ 62 'status' => 'error', 63 'errors' => $errors 64 ]; 65 } 66 67 return $result; 68} 69
###試したこと2
今度は次のようにテーブルロックをやめたところ、処理はすぐに終わり問題解決しました。
しかし質問のためにミニマムなコードにしましたが、実際テーブルロックは必須です。
// ajax処理 add_action( 'wp_ajax_my_ajax_update_actions_test', 'my_ajax_update_actions_test' ); function my_ajax_update_actions_test() { global $wpdb; $status = 'unexecuted'; $result = ['status'=>$status]; try { $errors = []; // JSから値を受け取る $action_info = $_POST['action_info'] ? $_POST['action_info'] : []; // wp_actions_testの更新 $inserted = insert_actions_test($action_info); if(isset($inserted['errors'])){ $errors['inserted'] = [ 'reason' => 'wp_actions_testの更新でエラー', 'result' => $inserted ]; } if( !empty($errors) ){ throw $e; }else{ $result = [ 'status' => $inserted['status'], 'action_info' => $inserted['action_info'], ]; } } catch (Exception $e) { $result = [ 'status' => 'error', 'errors' => $errors ]; } echo json_encode($result,JSON_UNESCAPED_UNICODE); die(); } } // アクションテーブル更新 function insert_actions_test($actions){ // 該当のソースコードと同じ }
###質問まとめ
なぜ自前バージョン
で問題が生じるのか、そしてどのように解決させるのか、お分かりになる方がいらっしゃいましたら、ご回答どうぞ宜しくお願い致します。
###環境
WordPress 5.3.3
PHP 7.2.13
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/01/21 03:00
2021/01/23 08:51
2021/01/23 08:51
2021/01/24 23:38