echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【JavaScript/MySQL】Falsyな値の扱い

はじめに

今回は、プログラミング言語のいわゆる「Falsyな値」について、JavaScriptMySQLにおける扱い&注意点についてです。

前提:「Falsyな値」って?

Falsyな値とは、下記のような値を指します。

  • 空文字
  • 0
  • false
  • null
  • undefined

表現が難しいのですが「空の値と判定される」とか「falseと判定される」ような値、でしょうか

JavaScript

よく使う使われ方

JavaScriptでこれらの値を使うケースとして「何か値が入っているか」を判定する処理があります。

具体的には、下記のような処理です。

// 何か値を取得する処理
const value = getValue();
  
if (value) {
  // 何か値が入っているとき
  console.log(`value is ${value}`);
} else {
  // 何も値が入っていないとき
  console.log('value is empty');
}
  

// 値が入っている場合もbooleanで判定したい場合はこんな感じ
if (!!value) {
  // 何か値が入っているとき
  console.log(`value is ${value}`);
} else {
  // 何も値が入っていないとき
  console.log('value is empty');
}

nullとundefinedだけ判定したい

ただ、上記の方法だと仮に空文字、0、falseといった値が入っていても「なにも値が入っていない」と判定されてしまい、ちょっと困ったことになります。

実際の入力フォームでも、0およびfalseあたりは入力値として設定することも多いので(例えば下記)、「なにも値が入っていない」と判定されるのは問題になる場合があります。

  • 飛行機や新幹線での「子供の人数」(=ビジネス目的ではたいてい0人)
  • アンケートなどでの「メルマガ受信を希望する」(=興味ない相手からは受け取りたくない)

こういう時にJavascriptでは「??(=Nullish Coalescing)」を使って判定することができます。(具体的に使用方法は下記参照)

詳細を知りたい場合、ECMAScriptの下記GitHubが参考になります。

GitHub - tc39/proposal-nullish-coalescing: Nullish coalescing proposal x ?? y

// ??は、左側がnullでもundefinedでもない場合は
// 左側の値をそのまま返す。
// 左側の値がnullまたはundefinedの場合のみ右側の値を返す。
const x = 0
console.log(x ?? -1);  // 0
  
const y = null
console.log(y ?? -1);  // -1
  
// ??を使い、こう書き換えることができる。
const z = getValue();

if ((z ?? null) !== null) {
  // 何か値が入っているとき
  console.log(`z is ${value}`);
} else {
  // 何も値が入っていないとき
  console.log('z is empty');
}

値の判定は===(型も比較)で

Falsyな値の比較ですが、これはタイトル通りで、必ず===(型も比較する)で比較してください。

==(値のみの比較)だと「どっちもFalsyな値」みたいな判定をされ、正しく判定できないからです。

実際のソースが下の画像となります。(CodeSandBoxで実行)

Falsyな値同士の比較は==だと、値が違ってもtrueになってしまうのが分かります。(===だとちゃんとfalseになる)
また、null/undefinedとの比較は==でもちゃんとfalseになるようです。

またFalsyな値同士の比較、及びundefinedとの比較の場合、==だと「===を使ってね」みたいな警告も表示されてますね。

MySQL

テーブル定義

MySQLの説明では、下記定義のテーブルを元に説明します。

フィールド名 説明 備考
id INT 主キー これは一意の値を入れるだけ
name VARCHAR2(45)
flag BOOLEAN
num INT

0(=INT型)とfalse(=BOOLEAN型)

続いてMySQLですが、MySQLでは数値型(INT)と論理型(BOOLEAN)は、ともに数値として扱われます。
なので数値型フィールドにfalseを指定したり、あるいは論理型フィールドに0を指定しても、特に型エラーなどは発生しません。

下記の結果が分かりやすいと思います。(id: 1のデータは関係ないのでスルーしてください)

# flagに0(=数値)、numにfalse(=論理値)を入れる。
# 特にエラーもなく登録できる
mysql> INSERT INTO test_table(id, name, flag, num) values(2, 0, 0, false);
Query OK, 1 row affected (0.01 sec)
  
# 結果を取得すると、どちらも0(=数値になっている)
mysql> SELECT * FROM test.test_table;
+----+--------+------+------+
| id | name   | flag | num  |
+----+--------+------+------+
|  1 | yamada |    0 |  100 |
|  2 | 0      |    0 |    0 |
+----+--------+------+------+
2 rows in set (0.00 sec)

MySQLでは、BOOLEAN型は内部的にはTINYINTとして扱われるようなので、その影響だと思われます。
実際、型定義を見てもflagはTINYINTとなっています。

# flagのTypeがTinyintになっている
mysql> SHOW FULL COLUMNS FROM test_table;
+-------+-------------+--------------------+------+-----+
| Field | Type        | Collation          | Null | Key |
+-------+-------------+--------------------+------+-----+
| id    | int         | NULL               | NO   | PRI | 
| name  | varchar(45) | utf8mb4_0900_ai_ci | YES  |     | 
| flag  | tinyint     | NULL               | YES  |     | 
| num   | int         | NULL               | YES  |     |
+-------+-------------+--------------------+------+-----+
4 rows in set (0.00 sec)

また、公式サイトでも「TRUE および FALSE 定数はそれぞれ 1 と 0 として評価されます」という記載があります。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 9.1.5 boolean リテラル

空文字は?

最後に空文字ですが、MySQL 5.7以上では、空文字をINT及びBOOLEANフィールドに登録しようとするとエラーになります。

ちなみに、文字列型フィールドに0やfalseを指定すると、「数値の文字列」として登録されるようです。

# BOOLEAN型に空文字を入れると、エラーになる
mysql> INSERT INTO test_table(id, name, flag, num) values(2, 0, '', false);
ERROR 1366 (HY000): Incorrect integer value: '' for column 'flag' at row 1
  
# INT型も同様にエラーになる
mysql> INSERT INTO test_table(id, name, flag, num) values(3, false, 0, '');
ERROR 1366 (HY000): Incorrect integer value: '' for column 'num' at row 1

が、MySQL5.6以下だと、0として普通に登録されます。(下画像参照。なおMySQL5.6の環境が手元にないので、DBFiddle で実行)

これは結構バグの原因になったりするので、気を付けた方が良いと思います。(そういう意味で、5.7からはエラーを吐くように変更されたのかも。)

まとめ

以上、JavaScriptMySQLでのFalysな値の扱いでした。

MySQLはともかく、JavaScriptはうっかり意図しない挙動になりかねないような仕様だと思うので、気を付けないといけませんね。

ただこれに限らず、JavaScriptはそういうのが結構ある言語とは思いますが...

では、今回はこの辺で。