質問文に対してシンプルに解決策を出すなら、
SQL文には列別名(as)が存在します。
SQL
1SELECT *
2FROM spendings
3ORDER BY date DESC, id DESC;
4SELECT SUM(price) as sum
5FROM spendings
6WHERE DATE_FORMAT(date,'%Y%m') = DATE_FORMAT(NOW(),'%Y%m')`
シンプルな列別名が与えられたので、簡単にアクセスできるようになりました。
ejs
1<div class="container-text">
2 <h2>支出リスト</h2>
3 <h1>今月の支出は
4 <span class="sum">
5 <%= sum %>
6 </span>
7 円です
8 </h1>
9</div>
ですが……
なんじゃこりゃ、SQLインジェクションを自分で引き起こしてるじゃん。
誰だよこんな書き方を質問者さんに勧めたヤツは。
https://www.npmjs.com/package/mysql#multiple-statement-queries
Multiple statement queries
Support for multiple statements is disabled for security reasons (it allows for SQL injection attacks if values are not properly escaped).
To use this feature you have to enable it for your connection:
和訳: 複数のステートメントのサポートは、セキュリティ上の理由から無効になっています (値が適切にエスケープされていない場合、SQL インジェクション攻撃が可能になります)。
この機能を使用するには、接続に対して有効にする必要があります。
2個のSQL文は個別に投げましょう。
その為のベストプラクティスまで解説していきます。
それを踏まえて手直しするとこんな感じ。
js
1app.get('/index', (req, res) => {
2 connection.query(
3 `SELECT *
4 FROM spendings
5 ORDER BY date DESC, id DESC;`
6 (err, spendings) => {
7 if (err) console.error(err);
8 connection.query(
9 `SELECT SUM(price) as sum
10 FROM spendings
11 WHERE DATE_FORMAT(date,'%Y%m') = DATE_FORMAT(NOW(),'%Y%m')`,
12 (err, results) => {
13 if (err) console.error(err);
14 res.render('index.ejs', {spendings, sum: results[0].sum});
15 }
16 );
17 }
18 );
19});
2個のSQL文を投げるだけでご覧の有様です。
こういうコールバック関数を引数として渡す機能を使い、
それがネストしていってコードが汚れる事を「コールバック地獄」と呼びます。
これに関してはPromiseとasync/awaitを覚えましょう。
JavaScriptではコールバック地獄に対して
Promiseというオブジェクト指向プログラミングのテクニックを用いた解決策があり、
async/await構文というPromiseを上手く使って同期処理っぽく非同期処理を書ける構文も用意されています。
(ECMAScript2017という2017年のバージョンで追加されました)
そして世のコード・ライブラリの多くがPromise前提で書き直されていきました。
従来のコールバック関数を引数として渡す方法は古臭い手法になりつつあります。
Node.jsのmysqlもその一つで3年前からもうメンテナンスされておらず
作者はmysql2というライブラリへ移行しました。
mysql2ではPromiseを使った最新の書き方があるのでそちらも見ていきましょう。
bash
1$ npm install mysql2
プロジェクト説明のREADMEを眺めても使い方は分かりますが、
GitHubのexamplesにはより詳細な使い方が載っているのでそこから抜粋しつつ手直ししましょうか
https://github.com/sidorares/node-mysql2/tree/master/examples/promise-co-await
js
1const mysql = require('mysql2/promise');
2
3// createConnectionがPromiseを返すようになっている
4// awaitしなければコネクションを取り出せなくなっている事に注意
5const connection = mysql.createConnection({host:'localhost', user: 'root', database: 'test'});
6
7// await構文を使う為にはasync関数を定義しなければならない
8app.get('/index', async (req, res) => {
9 // promiseインスタンスにawait構文を使うと、
10 // promise.then(val => {})のvalを引っ張り出すのと同じ効果を得られる
11 const conn = await connection;
12
13 // tryはpromiseをawaitした時、promiseが失敗していた時にキャッチできるようになる
14 try {
15 const [spendings] = await conn.query(`
16 SELECT *
17 FROM spendings
18 ORDER BY date DESC, id DESC;
19 `);
20 const [results] = await conn.query(`
21 SELECT SUM(price) as sum
22 FROM spendings
23 WHERE DATE_FORMAT(date,'%Y%m') = DATE_FORMAT(NOW(),'%Y%m')
24 `);
25 res.render('index.ejs', {spendings, sum: results[0].sum});
26 } catch (err) {
27 console.error(err);
28 }
29});