質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.50%
MySQL

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

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

3654閲覧

node.jsとMySQLでクエリを複数実行したい

退会済みユーザー

退会済みユーザー

総合スコア0

MySQL

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

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2018/03/20 10:08

編集2018/03/20 15:36

node.js:v9.5.0
MySQL:5.7.21
MacOS:10.12

見よう見まねでこういうコードを書きました。

javascript

1'use strict'; 2 3const mysql2 = require('mysql2/promise'); 4 5const pool = mysql2.createPool({ 6 host: 'localhost', 7 user: 'foo', 8 password: '', 9 database: 'test' 10}); 11 12const test = async () => { 13 const job = async () => { 14 const conn = await pool.getConnection(); 15 16 await conn.query('SELECT * FROM hoge_1', (err, rows, fields) => { 17 if (err) throw err; 18 console.log(rows); 19 }); 20 21 await conn.query('SELECT * FROM hoge_2', (err, rows, fields) => { 22 conn.release(); 23 if (err) throw err; 24 console.log(rows); 25 }); 26 }; 27 28 await job(); 29 await pool.end(); 30}; 31 32test();

やりたいことはnode.jsでMySQLを使ってデータを取得したいのですが、一つのプロセスでクエリを複数実行したいです。

async/awaitを使うことが条件となります。

期待していた結果は、job()内のawaitが上から順に実行されていき、クエリhoge_1、hoge_2の結果が落ちてくると思っていました。
ところがhoge_1の結果のみ返ってきて、pool.end()が効いていないせいか、処理を完了せず待機状態のままになってしまいます。

 
本当ならSQL文のWHERE以降の条件式に変数をもってきて、for文でループしたいのですが、うまくいかなくてバラバラにして試しています。

一度の処理でクエリを複数回すことは無理なのでしょうか?

 
追記:
ヒントを下さった皆様、ありがとうございました。

突然asyncモジュール禁止、async/awaitを使えと言われ、ドキュメントをきちんと把握せずに質問してしまい、申し訳ございませんでした。

ちょっとシンプルに変更した完成版のコードを貼っておきます。

javascript

1'use strict'; 2 3const mysql = require('mysql2/promise'); 4 5const pool = mysql.createPool({ 6 host: 'localhost', 7 user: 'foo', 8 password: '', 9 database: 'test' 10}); 11 12const test = async () => { 13 const conn = await pool.getConnection(); 14 15 const [rows_1, fields_1] = await conn.query('SELECT * FROM hoge_1'); 16 console.log(rows_1); 17 18 const [rows_2, fields_2] = await conn.query('SELECT * FROM hoge_2'); 19 console.log(rows_2); 20 21 const [rows_3, fields_3] = await conn.query('SELECT * FROM hoge_3'); 22 console.log(rows_3); 23 24 await pool.end(); 25} 26 27test();

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

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

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

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

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

guest

回答3

0

手元で動かせないので、コードを見ての推測になってしまいますが

await conn.query('SELECT * FROM hoge_1', (err, rows, fields) => { if (err) throw err; console.log(rows); });

なぜPromise(async/await)を利用しているのにコールバック関数を渡しているのでしょうか?
こちらを見ても、queryメソッドの引数はクエリとパラメータのみになっているようですが。

async/awaitを使用するのであれば

const [rows, fields] = await conn.query('SELECT * FROM hoge_1');

が正しいかと思います(参考

投稿2018/03/20 10:48

k.tada

総合スコア1679

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

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

退会済みユーザー

退会済みユーザー

2018/03/20 15:41

回答をありがとうございました。 最初にドキュメントを見ておくべきでした、スミマセン。。。
guest

0

awaitがどういう動きするのかをちゃんと覚えればすぐ解決する話だと思います。

コードを見た限り何にでもawait付けてますが、
awaitは非同期通信をよしなに処理してくれる魔法のロジックではありません。
awaitの特徴は下記です。

  • 右辺はPromise
  • resolveが実行されるまでひたすら遅延、resolve(value)を式の値として処理を継続する
  • rejectが実行されればreject(err)errを例外として投げ処理を抜ける

従って、awaitを使う場面ではawaitの右辺はPromiseであることが絶対条件です。
上記のコードはパット見いくつかPromiseを返さないように見えます。
なので、全てPromiseを返す関数を実行している事を確かめてください。

特にダメそうなのがk.tadaさんも仰ってるmysql2のqueryにコールバック関数乗せちゃってる箇所。
コールバックがない場合mysql2はPromiseを返す挙動になっているはずですが、
コールバック関数を指定した場合の挙動は私は把握してませんね…もし気になるならソースコードを実際に読んでみてください。


【おまけ】 完成版コードへの軽いレビュー

お疲れ様でした、ぐっと見やすい良いコードになりましたね。
折角なのでこの完成版コードを軽く見ていきます。

  • poolは直接queryメソッド叩いて使える
  • Promise.allと併用して並列処理っぽく
  • Promiseをリスト操作で生成する
  • asyncで作った関数はresolve, rejectを意識する

poolは直接queryメソッド叩いて使える

もう見たまんまです。
connを取り出しても良いですが、このまま使った方が楽ちんでしょう。

JavaScript

1const test = async () => { 2 const [rows_1, fields_1] = await pool.query('SELECT * FROM hoge_1'); 3 console.log(rows_1); 4 5 const [rows_2, fields_2] = await pool.query('SELECT * FROM hoge_2'); 6 console.log(rows_2); 7 8 const [rows_3, fields_3] = await pool.query('SELECT * FROM hoge_3'); 9 console.log(rows_3); 10 11 await pool.end(); 12}

Promise.allと併用して並列処理っぽく

まずはこのコードをデベロッパーツールに突っ込んで結果を見て下さい。
戻り値がPromiseであることが確認出来ます。
(Numberの配列なんで即resolvedしてますが)

JavaScript

1Promise.all([123]); 2// Promise {<resolved>: Array(1)}

つまり、このように書けば並列処理になります。
これならばpoolでコネクションを複数本引っ張った意味も出来るでしょう。
(ちょっとこれを走らせて速度を確認してみてください。)

JavaScript

1const test = async () => { 2 const results = await Promise.all([ 3 pool.query('SELECT * FROM hoge_1'), 4 pool.query('SELECT * FROM hoge_2'), 5 pool.query('SELECT * FROM hoge_3') 6 ]); 7 console.log(results); 8 9 await pool.end(); 10}

Promiseをリスト操作で生成する

pool.query何回も実行してますが、これはダサいです。
sqlの配列から動的に作るようにしましょう。

前項ではコードがキモい感じでしたが、
多少そのキモさが和らいでるのが確認できます。

JavaScript

1const test = async () => { 2 const sqls = [ 3 'SELECT * FROM hoge_1', 4 'SELECT * FROM hoge_2', 5 'SELECT * FROM hoge_3' 6 ]; 7 const promises = sqls.map(sql => pool.query(sql)); 8 const results = await Promise.all(promises); 9 console.log(results); 10 11 await pool.end(); 12}

asyncで作った関数はresolve, rejectを意識する

これは実践とはかけ離れた条件下なので、既にやっていることかもしれませんが、
asyncはawaitの逆、つまり絶対にpromiseを返す関数として振る舞います。

  • returnで値を返せばresolve(value)と等価
  • throwで例外を投げればreject(err)と等価

特にtestの外でコネクションを張っておきながら、testの中で閉じるのもおかしな話です。
実践を見据えてその辺を解消していきます。

JavaScript

1const mysql = require('mysql2/promise'); 2 3const pool = mysql.createPool({ 4 host: 'localhost', 5 user: 'foo', 6 password: '', 7 database: 'test' 8}); 9 10const test = pool => { 11 const sqls = [ 12 'SELECT * FROM hoge_1', 13 'SELECT * FROM hoge_2', 14 'SELECT * FROM hoge_3' 15 ]; 16 return Promises.all(sqls.map(sql => pool.query(sql))); 17} 18 19test(pool) 20 .then(results => { 21 console.log(results); 22 pool.end(); 23 }) 24 .catch(err => { 25 console.error(err); 26 pool.end(); 27 });

リファクタリング1号としてはこんな感じになりました。
ついでにtest関数がPromise.allを直接返すのでasync指定が剥げちゃいましたね…

まぁ、関数ってのは値を入れたら結果を返すものなので、
このようにsql結果を受け取り、.then.catchを使って値を調査、poolを閉じた方が設計としては綺麗だと思います。
実践は色々また状況が違うので最適なコードも変わってくると思います、頑張ってください!

投稿2018/03/20 11:50

編集2018/03/21 07:35
miyabi-sun

総合スコア21158

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

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

退会済みユーザー

退会済みユーザー

2018/03/20 15:40

回答をありがとうございました。参考になりました。
退会済みユーザー

退会済みユーザー

2018/03/21 10:08

Promises.allの使い方がよくわかりました。 ありがとうございます! とても参考になりました。
guest

0

ベストアンサー

こちらを参考にまずは書き直してみてはいかがでしょう?

ES7 Async Await - Promise wrappers / node-mysql2

https://github.com/sidorares/node-mysql2/blob/master/documentation/Promise-Wrapper.md#es7-async-await

投稿2018/03/20 11:58

HayatoKamono

総合スコア2415

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

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

退会済みユーザー

退会済みユーザー

2018/03/20 15:39

回答をありがとうございました。 Promise.allは便利そうですね。 ただselect sleep(2)、select sleep(3)とあるところにSELECT * FROM hoge文を入れてみたのですが、並列処理のせいか確認することができませんでした。。。 また必要になった時に試してみます、ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問