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

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

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

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

Node.js

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

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

Q&A

解決済

1回答

1469閲覧

expressを使って、DBから取得した値のグラフと、最新データを同時に表示したい。connection.query()内にconnection.query()を書きsql文を2回実行するのは可能か。

ccchogeee

総合スコア11

MySQL

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

Node.js

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

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

0グッド

0クリップ

投稿2021/11/11 06:46

編集2021/11/12 07:43

前提・実現したいこと

expressを使って、DBにある温度/湿度のデータからグラフを作成しようとしています。
また、温度/湿度のグラフに加えて、「最新 温度:20℃/湿度:40%」 というようにグラフとは別に最新の温度/湿度がひとめで分かるようにページ内に表示したいです。

/selectにpostして
sqlを実行し、グラフをindex.ejsで表示することはできました。

最新のデータを取得して表示しようと思ったときに、
app.jsのconnection.query()のなかに、もうひとつconnection.query()を書いてsqlを実行したいと考えました。このとき、console.log(results_graph);とconsole.log(results_new);では値は取れています。

しかし、index.ejsではエラーが出てブラウザが表示されなくなります。
(エラーは以下参照)

connection.query()のなかに、もうひとつconnection.query()を書くにはどうすればよいのでしょうか?

もしくは、それ以外の方法で実現可能な方法があればご教示いただきたいです。
よろしくお願いします。

※deviceを選択して/selectへpostするようになっています。

該当のソースコード

app.js
index.ejsにて、newsまわりの処理を書かないとき、(下記のindex.ejsコード参照)
console.log(results_graph);とconsole.log(results_new);では値は表示できます。

javascript

1const express = require('express'); 2const mysql = require('mysql'); 3const app = express(); 4 5app.use(express.static('public')); 6app.use(express.urlencoded({extended: false})); 7 8app.use(express.static('statics')); 9 10const connection = mysql.createConnection({ 11 host: 'localhost', 12 user: 'root', 13 password: '-------', 14 database: '********' 15}); 16 17app.get('/', (req, res) => { 18 res.render('top.ejs'); 19}); 20 21//これだけだとうまくいきます 22// app.post('/select', (req, res) => { 23// connection.query( 24// 'SELECT date, temperature, humidity FROM data WHERE device = ? ORDER BY date ASC;', 25// [req.body.device], 26// (error, results) => { 27// res.render('index.ejs', {items: results}); 28// } 29// ); 30// }); 31 32//selectに来たときに2つのSQLを実行したい 33app.post('/select', (req, res) => { 34 connection.query( 35 'SELECT date, temperature, humidity FROM data WHERE device = ? ORDER BY date ASC;', 36 [req.body.device], 37 (error_graph, results_graph) => { 38 console.log(results_graph); 39 40 connection.query( 41 'SELECT * FROM data where date=(select max(date) from data where device = ?);', 42 [req.body.device], 43 (error_new, results_new) => { 44 console.log(results_new); 45 res.render('index.ejs', {items: results_graph, news: results_new}); 46 } 47 ); 48 } 49 ); 50}); 51 52app.listen(3000); 53

index.ejs(<body>の一部を抜粋)うまくいく

ejs

1<% items.forEach((item) => { %> 2 <span class="id-column"><%= item.id %></span> 3 <span class="dt-column"><%= item.date %></span> 4 <span class="temp-column"><%= item.temperature %></span> 5 <span class="humi-column"><%= item.humidity %></span> 6<% }); %> 7

index.ejs(<body>の一部を抜粋)newsの中身を表示したくてもエラーが出る

ejs

1<% news.forEach((new) => { %> 2 <span class="id-column"><%= new.id %></span> 3 <span class="dt-column"><%= new.date %></span> 4 <span class="temp-column"><%= new.temperature %></span> 5 <span class="humi-column"><%= new.humidity %></span> 6<% }); %> 7<% items.forEach((item) => { %> 8 <span class="id-column"><%= item.id %></span> 9 <span class="dt-column"><%= item.date %></span> 10 <span class="temp-column"><%= item.temperature %></span> 11 <span class="humi-column"><%= item.humidity %></span> 12<% }); %> 13

エラー内容

ターミナルとchrome上に以下のエラーが表示されます。

SyntaxError: Unexpected token ')' in /Users/pass/views/index.ejs while compiling ejs If the above error is not helpful, you may want to try EJS-Lint: https://github.com/RyanZim/EJS-Lint Or, if you meant to create an async function, pass `async: true` as an option. at new Function (<anonymous>) at Template.compile (/Users/pass/node_modules/ejs/lib/ejs.js:662:12) at Object.compile (/Users/pass/node_modules/ejs/lib/ejs.js:396:16) at handleCache (/Users/pass/node_modules/ejs/lib/ejs.js:233:18) at tryHandleCache (/Users/pass/node_modules/ejs/lib/ejs.js:272:16) at View.exports.renderFile [as engine] (/Users/pass/node_modules/ejs/lib/ejs.js:489:10) at View.render (/Users/pass/node_modules/express/lib/view.js:135:8) at tryRender (/Users/pass/node_modules/express/lib/application.js:640:10) at Function.render (/Users/pass/node_modules/express/lib/application.js:592:3) at ServerResponse.render (/Users/passh/node_modules/express/lib/response.js:1012:7)

試したこと

async/awaitなどが必要なのかなと考え、似たようなことを解決しようとしているサイトをみてみましたが、javascriptの経験が浅く、修正方法がわかりませんでした。
以下のようにすると、/selectに行こうとしたときに、止まって先に進めなくなってしまいます。

javascript

1app.post('/select', (req, res) => { 2 ( async () => { 3 await connection.query( 4 'SELECT date, temperature, humidity FROM data WHERE device = ? ORDER BY date ASC;', 5 [req.body.device], 6 (error_graph, results_graph) => { 7 console.log(results_graph); 8 9 connection.query( 10 'SELECT * FROM data where date=(select max(date) from data where device = ?);', 11 [req.body.device], 12 (error_new, results_new) => { 13 console.log(results_new); 14 res.render('index.ejs', {items: results_graph, news: results_new}); 15 } 16 ); 17 } 18 ); 19 }); 20});

よろしくお願いします。


※編集・追記依頼をいただきました

SyntaxError: Unexpected token ')' in /Users/pass/views/index.ejs while compiling ejs

質問時に提示した上記のエラーに関しては、
index.ejs内で「new」を使用していたのが原因でした。
「new」の使用をやめ、その他のエラーを修正したところ、acync/awaitを用いて要件を実現することができました!
ありがとうございます。

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

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

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

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

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

hoshi-takanori

2021/11/11 12:35

index.ejs で使ってる new という変数名が JavaScript のキーワードと衝突してる気がするので、別の名前にすれば良いのでは。
ccchogeee

2021/11/12 07:39

おっしゃるとおりで、「new」の使用をやめ、その他のエラーを修正しましたところうまく行きました! ありがとうございます!
guest

回答1

0

ベストアンサー

結論にメスを突き立てると

エラー文がSyntaxError: Unexpected token ')' in /Users/pass/views/index.ejs while compiling ejsですね。
これを意訳するとこんな感じです。

index.ejsファイルをindex.htmlに作り変えようと思ったけど
括弧の対応狂ってるやろ、なんやこの')'は?

もしMySQL実行箇所が原因ならエラー文の下にずらずら載ってるスタックトレースが
app.jsを指すはずなんですよね。

以上の事からMySQL実行箇所を睨んでも多分無駄です。
多分質問文外の所に問題があると思いますよ。
index.ejsファイルを確認してみてください。

まぁ、それだけだと何なのでapp.jsもかっこよく作り変えましょうか。


まずブラウザの裏でHTMLを書き換える目的で作られたJavaScriptには
何かを待つという機能が存在しません

なので「待つ」を実現する為にJavaScriptの仕様はイベントを選択しました。
イベント置き場に「達成条件」と「実行して欲しい関数」を2個セットにして覚えさせておき、
達成したら関数を勝手に実行してねという思想で解決します。
詳しくはイベントループみたいなワードでググってください。

js

1connection.query( 2 'SELECT date, temperature, humidity FROM data WHERE device = ? ORDER BY date ASC;', 3 [req.body.device], 4 () => {} // これはコールバック関数 5);

何だって関数実行の引数に、実行してない関数渡してやる必要があるんだ?
これが巡り巡ってイベントの達成条件と対の実行して欲しい関数になるからです。

そこから「コールバック地獄」→「Promise」の登場と採用→「async/await」構文の追加
こういう経緯を辿ります。

async/awaitでやりたい

質問文でもasync/awaitでやろうと頑張った跡が見えますが、
それじゃ何が嬉しいか全く理解出来ないと思います。

awaitがPromiseインスタンスの.thenメソッドを叩いて第一引数を取り出して、
コードの見てくれだけ同期処理に戻す糖衣構文ですので、
Promiseを学習してない人は使っちゃダメです。

とりあえず良い感じに動くように手を入れるので、
後でしっかり勉強して飲み込んでください。

async/awaitなどが必要なのかなと考え、似たようなことを解決しようとしているサイトをみてみましたが、javascriptの経験が浅く、修正方法がわかりませんでした。

まず、npmで配布されているmysqlモジュールは2年前に開発者達が捨てた残骸なので、
移行先のmysql2モジュールを使うようにしてください。
これはNode.jsの黎明期にいくつかのMySQLモジュールを作っているチーム同士が手を取り合った時に、mysql2へ移行したという経緯があります。

利用者側が「また覚え直しかよ……」とならないよう、
mysql2はmysqlと同じメソッドや引数で扱えるようになっています。
安心ですね。

それもmysql2使えという理由の一つですが、
mysql2にはPromise動作版が搭載されています。

先程async/await使うならPromise覚えてねと言いましたが、
ライブラリがPromiseに対応してないと
コールバック引数駆動の機能を一度自分のコードでPromiseっぽくラッピングするという超絶二度手間で死にます。
なので質問文の頑張りではまるで足りてないわけです。

それではmysql2を導入してpromise -> async/awaitとステップアップさせましょう。

bash

1$ npm install mysql2

まずPromise版を使ったPromise純正流儀で記述していきます。

js

1// mysql2にはPromise動作モードというのがある 2const mysql = require("mysql2/promise"); 3 4// ここはこのままで構わない 5// ただし返り値がPromiseのインスタンスになっており、一度thenを叩いて本物のコネクションを取り出す必要がある 6const connection = mysql.createConnection({ 7 host: 'localhost', 8 user: 'root', 9 password: '-------', 10 database: '********' 11}); 12 13app.post('/select', (req, res) => { 14 const variables = {}; 15 16 // connection.thenを基点に展開する 17 connection.then(c => c.query( 18 'SELECT date, temperature, humidity FROM data WHERE device = ? ORDER BY date ASC;', 19 [req.body.device] 20 )).then(results => { 21 console.log(results); 22 variables.items = results; 23 24 return connection.then(c => c.query( 25 'SELECT * FROM data where date=(select max(date) from data where device = ?);', 26 [req.body.device] 27 )); 28 }).then(results => { 29 console.log(results); 30 variables.news = results; 31 32 res.render('index.ejs', valiables); 33 }); 34});

Promiseのインスタンスは.thenメソッドを使う事で数珠つなぎに出来ます。
コールバック関数を引数に渡すやり方に比べればネストが一段階下がった事がわかりますね。

「で?このPromise使った書き方の何が嬉しいんだい?」

そうなんですよね、Promiseはコールバック地獄を解決しただけで
アプリ開発者目線では別に何も嬉しくなってないんですよね。

Promiseはasync/await構文と併用して、初めて凄まじい威力を発揮するようになります。

js

1const mysql = require("mysql2/promise"); 2const connection = mysql.createConnection({ 3 host: 'localhost', 4 user: 'root', 5 password: '-------', 6 database: '********' 7}); 8 9// await構文はasync関数の直下でないと使えない 10// 普通のアロー関数の前にasyncというワードを付与するだけ 11app.post('/select', async (req, res) => { 12 // awaitを使うとPromise.then(val => {})を実行してvalを取り出すコードに変換される 13 const c = await connection; 14 const items = await c.query( 15 'SELECT date, temperature, humidity FROM data WHERE device = ? ORDER BY date ASC;', 16 [req.body.device] 17 ); 18 const news = await c.query( 19 'SELECT * FROM data where date=(select max(date) from data where device = ?);', 20 [req.body.device] 21 ); 22 23 console.log(items); 24 console.log(news); 25 26 res.render('index.ejs', {items, news}); 27});

はいめっちゃ読みやすい。

でもまぁ、今度は'SELECT * FROM data where date=(select max(date) from data where device = ?);'が心配ですね。
本当に動くのかい?

try-catchで拾ってなんかエラー出したら怒るようにしておきましょうか。

js

1app.post('/select', async (req, res) => { 2 const val = {}; 3 try { 4 const c = await connection; 5 const items = await connection.query( 6 'SELECT date, temperature, humidity FROM data WHERE device = ? ORDER BY date ASC;', 7 [req.body.device] 8 ); 9 const news = await connection.query( 10 'SELECT * FROM data where date=(select max(date) from data where device = ?);', 11 [req.body.device] 12 ); 13 res.render('index.ejs', {items, news}); 14 } catch (e) { 15 console.log("SQLクエリの発行に失敗"); 16 console.error(e); 17 res.send('サーバーでの動作が大失敗しました。急いで直すのでしばしお待ちを。'); 18 } 19});

とりまこんな感じですかね。

投稿2021/11/11 12:50

編集2021/11/12 06:16
miyabi-sun

総合スコア21194

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

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

ccchogeee

2021/11/12 05:34 編集

大変ご丁寧な対応ありがとうございます!!!!! 詳しい内容はまだ理解できていないのですが、とりあえず教えていただいたコードで実装してみたところ、 クエリの部分に以下のようなエラーが出るようになりました。 ``` SQLクエリの発行に失敗 TypeError: connection.query is not a function ``` 手順としては npm install mysql2 を実行し、 const mysql = require('mysql'); ↓ const mysql = require("mysql2/promise"); と変更し、最後にいただいたコードへまるっと置き換えました。 const mysql = require('mysql');だとエラーは出ず、 const mysql = require("mysql2/promise");にすることでエラーになるようです。 mysql2を使用するために他に修正すべきところがあるのでしょうか?
ccchogeee

2021/11/12 07:37

ありがとうございます! その後また別のエラーが以下のように出ました TypeError: /Users/tamada/workspace/agri_graph/views/index.ejs:118 116| </div> --> 117| >> 118| <% var date = String(item.insert_date.toLocaleString());%> 119| <% selectDate.push(date);%> 120| <% selectTemp.push(item.temperature);%> 121| <% selectHumi.push(item.humidity);%> これは、itemsの形が、質問時自分が送っていたitemsと違う形で送られていたようで、 調べてみたところ、以下のように修正すると全てうまく行きました!!! const [items] = await connection.query(... const [news] = await connection.query(... 大変助かりました!ありがとうございます! promiseやasync/awaitのあたりもっと勉強します! ※質問時のエラー(SyntaxError: Unexpected token ')' in...)は、 他の方から修正依頼をいただいていたとおり、 「new」を使用していたためでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問