🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

2回答

1211閲覧

WordPressで自前のクエリを書きたい

hana_hana

総合スコア27

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

1クリップ

投稿2021/01/19 15:35

編集2021/01/23 08:43

###前提・実現したいこと
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

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

おそらくですが、テーブルロックを掛けているコネクションと、Insertするコネクションが異なるため、
テーブルロックが解除されるまで待ちになっているのではないでしょうか。

投稿2021/01/21 02:48

tabuu

総合スコア2471

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tabuu

2021/01/21 03:00

ちなみにですが、actionsにIDが無いのでupdateは発生しないと思います。
hana_hana

2021/01/23 08:51

> おそらくですが もしかして、次の部分でロックのコネクションが発生して、 ```PHP // テーブルロック $wpdb->query('SET autocommit=0'); $sql = 'LOCK TABLES wp_actions_test WRITE'; $locked = $wpdb->query($sql); ``` さらに次の部分で新たにコネクションが発生しているということですか? ```PHP // クエリの実行 $stmt = ''; $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME); ``` なるほど、ありえそうな予感がいたします。 しかしもしそうであれば、どのような解決が考えられますでしょうか? $wpdbによるロックを引きついだままで、独自クエリを実行しなければならないとなりますと…方法が思いつけません。
hana_hana

2021/01/23 08:51

> ちなみにですが 失礼しました。CREATEにユニーク制約を追加しておきました。
tabuu

2021/01/24 23:38

そもそもですが、(提示されている内容だけ見ると)テーブルロックの必要性がありません。 users_ID,content_id,action_name_idをキーにした複数のリクエストが同時に発生するのでしょうか? 仮に同時に発生してもis_deletedを0にするだけなので被っても問題ないのではないでしょうか? どうしてもロックしたいということであれば、wpdbでロックでずに mysqliで生成したコネクションでロックとINSERTを実行すればよいかと思います。
guest

0

throw $e$eってちゃんとExceptionなんですか?
そうでないならばthrowしてもcatchされないためにCOMMITROLLBACKもされず
autocommitも切ってるわけですからTRANSACTIONが終了されないまま
UNLOCK TABLESしようとして失敗してるんじゃないでしょうか?

あるいはトランザクションが開始してない状態で
COMMITROLLBACKしようとして失敗してるか

投稿2021/01/20 10:44

KazuhiroHatano

総合スコア7819

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hana_hana

2021/01/20 11:24 編集

ありがとうございます。質問のためにコードは一部変更していますが、$eはExcetionにいきます。 もしご指摘のようにエラーの場合にcatchされないと仮定しても、そもそも該当のソースコードではエラーが起こりませんから、エラーの場合という仮定の上での推測は少し違うのかなと思われます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問