📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

ページの作成:「== 概要 == MySQLでは、豊富な文字列関数が提供されており、文字列の結合、分割、検索、置換、フォーマット等の処理を行うことができる。<br> これらの関数は、SELECT文、UPDATE文、WHERE句等で使用でき、データ操作において重要な役割を果たす。<br> <br> MySQL 5.7とMySQL 8.0では、文字列関数の動作に一部差異がある。<br> 特に、文字コードと照合順序の扱い…」
 
編集の要約なし
1行目: 1行目:
== 概要 ==
== 概要 ==
MySQLでは、豊富な文字列関数が提供されており、文字列の結合、分割、検索、置換、フォーマット等の処理を行うことができる。<br>
MySQLの集約関数 (Aggregate Functions) は、複数の行から単一の値を計算する関数である。<br>
これらの関数は、SELECT文、UPDATE文、WHERE句等で使用でき、データ操作において重要な役割を果たす。<br>
集約関数は、<code>GROUP BY</code> 句と組み合わせて使用することで、データをグループ化して集計を行うことができる。<br>
<br>
<br>
MySQL 5.7とMySQL 8.0では、文字列関数の動作に一部差異がある。<br>
主要な集約関数には、<code>COUNT</code>、<code>SUM</code>、<code>AVG</code>、<code>MAX</code><code>MIN</code><code>GROUP_CONCAT</code> が含まれる。<br>
特に、文字コードと照合順序の扱いに変更がある。<br>
また、MySQL 5.7.22以降では、<code>JSON_ARRAYAGG</code> および <code>JSON_OBJECTAGG</code> が追加されている。<br>
MySQL 8.0では、デフォルト文字セットが <code>utf8mb4</code>、デフォルト照合順序が <code>utf8mb4_0900_ai_ci</code> に変更された。<br>
MySQL 5.7では、デフォルト文字セットは <code>latin1</code>、デフォルト照合順序は <code>latin1_swedish_ci</code> であった。<br>
<br>
<br>
文字列関数は、マルチバイト文字セット (utf8mb4) を考慮して使用する必要がある。<br>
集約関数は、<code>NULL</code> 値を自動的に無視する特性を持つ (一部例外を除く)。<br>
<code>LENGTH</code> 関数 と <code>CHAR_LENGTH</code> 関数は、マルチバイト文字での動作が異なる。<br>
全ての値が <code>NULL</code> の場合、集約関数は <code>NULL</code> を返す。(<code>COUNT(*)</code> は、0を返す)<br>
<code>LENGTH</code> 関数はバイト長を返し、<code>CHAR_LENGTH</code> 関数は文字数を返す。<br>
<br>
<br>
UTF-8エンコーディングでは、日本語は1文字あたり3バイトで表現されるため、<code>LENGTH</code>関数の戻り値は文字数の3倍となる。<br>
MySQL 8.0以降では、集約関数を <code>OVER()</code> 句と組み合わせてウインドウ関数として使用できる。<br>
ウインドウ関数として使用した場合、行を削減せず各行が保持される。<br>
<br>
<br>
文字列関数をWHERE句で使用すると、インデックスが使用されない場合が多い。<br>
<code>GROUP BY</code> 句を使用する際は、MySQL 8.0のデフォルトSQLモードである <code>ONLY_FULL_GROUP_BY</code> に注意が必要である。<br>
パフォーマンスを考慮する場合は、関数を使用しないクエリ設計を検討する必要がある。<br>
このモードでは、<code>SELECT</code> リストの非集約列は全て <code>GROUP BY</code> 句に含まれるか、関数従属である必要がある。<br>
<br>
<br>
<u>TEXT型およびBLOB型での文字列関数使用は、処理速度が低下する傾向がある。</u><br>
集約関数のパフォーマンスは、インデックスの有無、データ量、<code>DISTINCT</code> の使用などに影響される。<br>
<u>大量のテキストデータを処理する場合は、VARCHAR型の使用を推奨する。</u><br>
<code>EXPLAIN</code> で実行計画を確認し、<code>Using temporary</code>、<code>Using filesort</code> をチェックすることが推奨される。<br>
<br><br>
<br><br>


== 文字列結合 ==
== 基本構文 ==
==== CONCAT ====
MySQLで提供される主要な集約関数を以下に示す。<br>
<code>CONCAT</code> 関数は、複数の文字列を結合する。<br>
<br>
<br>
基本構文を以下に示す。<br>
<center>
<br>
{| class="wikitable"
<syntaxhighlight lang="sql">
|+ 主要な集約関数一覧
CONCAT(str1, str2, ...)
! 関数名 !! 説明 !! 戻り値型
</syntaxhighlight>
|-
| COUNT(*) || 全ての行数をカウント (NULL含む) || BIGINT
|-
| COUNT(column) || 指定列のNULL以外の値をカウント || BIGINT
|-
| COUNT(DISTINCT column) || 指定列の重複を除いた値をカウント || BIGINT
|-
| SUM(column) || 指定列の合計値を計算 (NULL除外) || DECIMAL または DOUBLE
|-
| SUM(DISTINCT column) || 指定列の重複を除いた合計値を計算 || DECIMAL または DOUBLE
|-
| AVG(column) || 指定列の平均値を計算 (NULL除外) || DECIMAL または DOUBLE
|-
| AVG(DISTINCT column) || 指定列の重複を除いた平均値を計算 || DECIMAL または DOUBLE
|-
| MAX(column) || 指定列の最大値を取得 || 列の型に依存
|-
| MIN(column) || 指定列の最小値を取得 || 列の型に依存
|-
| GROUP_CONCAT(column) || 指定列の値を連結した文字列を生成 || TEXT
|-
| JSON_ARRAYAGG(column) || 指定列の値をJSON配列として集約 (MySQL 5.7.22+) || JSON
|-
| JSON_OBJECTAGG(key, value) || キーと値のペアをJSONオブジェクトとして集約 (MySQL 5.7.22+) || JSON
|}
</center>
<br>
<br>
引数のいずれかが <code>NULL</code> の場合、結果は <code>NULL</code> となる。<br>
==== COUNT関数 ====
これは、<code>CONCAT_WS</code> 関数との重要な違いである。<br>
<code>COUNT</code> 関数は、行数をカウントする集約関数である。<br>
<br>
<br>
使用例を以下に示す。<br>
基本的な使用例を以下に示す。<br>
<br>
  <syntaxhighlight lang="mysql">
  <syntaxhighlight lang="sql">
-- 全ての行数をカウント (NULL含む)
  SELECT CONCAT('Hello', ' ', 'World');
  SELECT COUNT(*) FROM employees;
-- 結果: 'Hello World'
   
   
  SELECT CONCAT('ID: ', id, ' Name: ', name) FROM users;
-- 指定列のNULL以外の値をカウント
  SELECT COUNT(department_id) FROM employees;
   
   
SELECT CONCAT('MySQL', NULL, 'Database');
  -- 重複を除いたカウント
  -- 結果: NULL
  SELECT COUNT(DISTINCT department_id) FROM employees;
</syntaxhighlight>
<br>
<code>CONCAT</code> 関数は、数値型の引数を文字列に自動変換する。<br>
<br>
==== CONCAT_WS ====
<code>CONCAT_WS</code> 関数は、セパレータを使用して複数の文字列を結合する。<br>
<br>
基本構文を以下に示す。<br>
<br>
<syntaxhighlight lang="sql">
CONCAT_WS(separator, str1, str2, ...)
</syntaxhighlight>
<br>
<code>CONCAT</code> 関数と異なり、<code>CONCAT_WS</code> 関数は、<code>NULL</code> の引数をスキップする。<br>
セパレータが <code>NULL</code> の場合のみ、結果は <code>NULL</code> となる。<br>
<br>
使用例を以下に示す。<br>
<br>
<syntaxhighlight lang="sql">
  SELECT CONCAT_WS(', ', 'Apple', 'Banana', 'Cherry');
-- 結果: 'Apple, Banana, Cherry'
SELECT CONCAT_WS('-', '2025', '02', '15');
-- 結果: '2025-02-15'
SELECT CONCAT_WS(', ', 'Alice', NULL, 'Bob', 'Charlie');
-- 結果: 'Alice, Bob, Charlie' (NULLはスキップされる)
   
   
  SELECT CONCAT_WS(NULL, 'Alice', 'Bob', 'Charlie');
-- 複数列の組み合わせで重複を除いたカウント
-- 結果: NULL (セパレータがNULLのため)
  SELECT COUNT(DISTINCT department_id, job_id) FROM employees;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>CONCAT_WS</code> 関数は、CSV形式のデータ生成に便利である。<br>
<code>COUNT(*)</code> <code>COUNT(column)</code> の違いを以下に示す。<br>
<br><br>
 
== 部分文字列の取得 ==
==== SUBSTRING / SUBSTR / MID ====
<code>SUBSTRING</code> 関数、<code>SUBSTR</code> 関数、<code>MID</code> 関数は、文字列から部分文字列を取得する。<br>
これらの関数は、同一の動作をする別名である。<br>
<br>
<br>
基本構文を以下に示す。<br>
<center>
<br>
{| class="wikitable"
<syntaxhighlight lang="sql">
|+ <code>COUNT(*)</code> と <code>COUNT(column)</code> の違い
SUBSTRING(str, pos)
|-
SUBSTRING(str, pos, len)
! 関数 !! 説明
SUBSTR(str, pos, len)
|-
MID(str, pos, len)
| COUNT(*)
</syntaxhighlight>
|
* 全ての行数をカウントする
* <code>NULL</code> 値を含む全ての行を対象とする
|-
| COUNT(column) || 指定列の <code>NULL</code> 以外の値のみをカウントする。<br><code>NULL</code> 値は自動的に除外される。
|}
</center>
<br>
<br>
<center>
<center>
{| class="wikitable"
{| class="wikitable"
|+ 引数の意味
|+ パフォーマンス特性
|-
! 引数 !! 説明
|-
|-
| 第1引数 || 対象文字列
! ストレージエンジン !! 説明
|-
|-
| 第2引数 || 開始位置 (1始まり、負の値は末尾から)
| InnoDB || <code>COUNT(*)</code> は、最小の利用可能なセカンダリインデックスをスキャンする。<br>プライマリキーより小さいセカンダリインデックスが存在する場合、そちらを優先的に使用する。
|-
|-
| 第3引数 || 取得する文字数 (省略時は末尾まで)
| MyISAM || 内部的に行数を保持しているため、<code>WHERE</code> 句なしの <code>COUNT(*)</code> は高速に実行される。
|}
|}
</center>
</center>
<br>
<br>
使用例を以下に示す。<br>
戻り値の型は、MySQL 8.0以降では常に <code>BIGINT</code> である。<br>
<br>
<code>COUNT(DISTINCT column1, column2)</code> のように複数列を指定した場合、列の組み合わせでユニークな行をカウントする。<br>
<br>
==== SUM関数 ====
<code>SUM</code> 関数は、指定列の合計値を計算する集約関数である。<br>
<br>
基本的な使用例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT SUBSTRING('HelloWorld', 6);
-- 給与の合計
-- 結果: 'World'
  SELECT SUM(salary) FROM employees;
   
   
  SELECT SUBSTRING('HelloWorld', 1, 5);
-- 部署ごとの給与合計
-- 結果: 'Hello'
  SELECT department_id, SUM(salary) FROM employees GROUP BY department_id;
   
   
SELECT SUBSTRING('HelloWorld', -5);
  -- 重複を除いた合計
  -- 結果: 'World' (末尾から5文字)
  SELECT SUM(DISTINCT salary) FROM employees;
  SELECT SUBSTRING('HelloWorld', -5, 3);
-- 結果: 'Wor' (末尾から5文字目から3文字)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
負の位置指定により、末尾からの取得が可能である。<br>
<code>NULL</code> 値の処理を以下に示す。<br>
* <code>NULL</code> 値は自動的に除外される。
* 全ての値が <code>NULL</code> の場合、<code>SUM</code> は <code>NULL</code> を返す。
* <code>COUNT(column)</code> が0の場合、<code>SUM</code> は <code>NULL</code> を返す。
<br>
<br>
==== LEFT / RIGHT ====
戻り値の型を以下に示す。<br>
<code>LEFT</code> 関数は、文字列の左端から指定文字数を取得する。<br>
* 整数型または DECIMAL型の引数
<code>RIGHT</code> 関数は、文字列の右端から指定文字数を取得する。<br>
*: <code>DECIMAL</code> 型を返す。
* FLOAT型または DOUBLE型の引数
*: <code>DOUBLE</code> 型を返す。
<br>
<br>
基本構文を以下に示す。<br>
<u>※注意</u><br>
* <u>数値型の列にのみ適用可能</u>
*: 時間型 (TIME、DATE、DATETIME) に直接適用できない。
*: <code>TIME_TO_SEC()</code> 等で事前に数値に変換する必要がある。
* <u>オーバーフロー</u>
*: 整数型の合計が型の範囲を超える場合、オーバーフローが発生する可能性がある。
*: <code>DECIMAL</code> 型または <code>BIGINT</code> 型を使用することを推奨する。
<br>
<br>
  <syntaxhighlight lang="sql">
<code>SUM(DISTINCT column)</code> を使用すると、重複する値を除いて合計を計算する。<br>
  LEFT(str, len)
<br>
  RIGHT(str, len)
時間型の合計を計算する例を以下に示す。<br>
<br>
  <syntaxhighlight lang="mysql">
  -- TIME型の合計 (秒に変換)
  SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(work_time))) FROM daily_records;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
使用例を以下に示す。<br>
==== AVG関数 ====
<code>AVG</code> 関数は、指定列の平均値を計算する集約関数である。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
基本的な使用例を以下に示す。<br>
  SELECT LEFT('HelloWorld', 5);
  <syntaxhighlight lang="mysql">
-- 結果: 'Hello'
-- 給与の平均
  SELECT AVG(salary) FROM employees;
   
   
  SELECT RIGHT('HelloWorld', 5);
-- 部署ごとの給与平均
-- 結果: 'World'
  SELECT department_id, AVG(salary) FROM employees GROUP BY department_id;
   
   
SELECT LEFT('2025-02-15', 4);
  -- 重複を除いた平均
  -- 結果: '2025' (年を取得)
  SELECT AVG(DISTINCT salary) FROM employees;
  SELECT RIGHT('2025-02-15', 2);
-- 結果: '15' (日を取得)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
これらの関数は、<code>SUBSTRING</code> 関数の簡易版である。<br>
内部的な計算方法を以下に示す。<br>
* <code>AVG(column)</code> は、<code>SUM(column) / COUNT(column)</code> と同等である。
* <code>NULL</code> 値は自動的に除外される。
<br>
<br>
==== SUBSTRING_INDEX ====
戻り値の型を以下に示す。<br>
<code>SUBSTRING_INDEX</code> 関数は、デリミタを基準に文字列を分割し、指定された部分を取得する。<br>
* 整数型または DECIMAL型の引数
*: <code>DECIMAL(14, 4)</code> 型を返す。
* FLOAT型または DOUBLE型の引数
*: <code>DOUBLE</code> 型を返す。
<br>
<br>
基本構文を以下に示す。<br>
精度に関する注意を以下に示す。<br>
* 整数型の平均を計算する場合、小数点以下が切り捨てられない
*: <code>DECIMAL(14, 4)</code> で正確な小数値が返される。
* 丸め処理
*: <code>ROUND(AVG(column), 2)</code> で任意の桁数に丸めることができる。
<br>
<br>
<syntaxhighlight lang="sql">
<code>AVG(DISTINCT column)</code> を使用すると、重複する値を除いて平均を計算する。<br>
SUBSTRING_INDEX(str, delim, count)
</syntaxhighlight>
<br>
<br>
<center>
丸め処理の例を以下に示す。<br>
{| class="wikitable"
|+ 引数の意味
|-
! 引数 !! 説明
|-
| 第1引数 || 対象文字列
|-
| 第2引数 || デリミタ文字列
|-
| 第3引数 || デリミタの出現回数 (正数は左から、負数は右から)
|}
</center>
<br>
<br>
使用例を以下に示す。<br>
  <syntaxhighlight lang="mysql">
<br>
-- 小数点以下2桁に丸める
  <syntaxhighlight lang="sql">
  SELECT ROUND(AVG(salary), 2) FROM employees;
  SELECT SUBSTRING_INDEX('192.168.1.100', '.', 2);
-- 結果: '192.168' (左から2番目のピリオドまで)
   
   
  SELECT SUBSTRING_INDEX('192.168.1.100', '.', -2);
  -- 整数に切り捨て
  -- 結果: '1.100' (右から2番目のピリオドまで)
  SELECT FLOOR(AVG(salary)) FROM employees;
   
   
  SELECT SUBSTRING_INDEX('user@example.com', '@', 1);
-- 整数に切り上げ
  -- 結果: 'user' (ユーザ名部分を取得)
  SELECT CEIL(AVG(salary)) FROM employees;
</syntaxhighlight>
<br>
==== MAX / MIN関数 ====
<code>MAX</code> 関数 および <code>MIN</code> 関数は、指定列の最大値および最小値を取得する集約関数である。<br>
<br>
基本的な使用例を以下に示す。<br>
<syntaxhighlight lang="mysql">
  -- 最大給与
SELECT MAX(salary) FROM employees;
   
   
  SELECT SUBSTRING_INDEX('user@example.com', '@', -1);
  -- 最小給与
  -- 結果: 'example.com' (ドメイン部分を取得)
  SELECT MIN(salary) FROM employees;
   
   
  SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('192.168.1.100', '.', 3), '.', -1);
-- 部署ごとの最大給与・最小給与
  -- 結果: '1' (3番目のオクテットを取得)
  SELECT department_id, MAX(salary), MIN(salary)
  FROM employees
GROUP BY department_id;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
ネストして使用することで、複雑な分割が可能である。<br>
数値型以外への適用を以下に示す。<br>
<br><br>
* 文字列型
 
*: 辞書順 (照合順序に依存) で比較される。
== 文字列置換・挿入 ==
*: 照合順序 (COLLATION) によって結果が異なる場合がある。
==== REPLACE ====
* 日付型 (DATE、DATETIME、TIMESTAMP)
<code>REPLACE</code> 関数は、文字列内のすべての出現を置換する。<br>
*: 時系列順で比較される。
*: 最も古い日付 (<code>MIN</code>)、最も新しい日付 (<code>MAX</code>) を取得できる。
<br>
インデックスとの関係を以下に示す。<br>
* インデックスが存在する列に対して <code>MAX</code> / <code>MIN</code> を使用する場合、インデックスを活用した高速な検索が可能である。
* これは、<code>ORDER BY column DESC LIMIT 1</code> (<code>MAX</code>) や <code>ORDER BY column ASC LIMIT 1</code> (<code>MIN</code>) と同等の最適化が行われるためである。
<br>
<code>NULL</code> 値の処理を以下に示す。<br>
* <code>NULL</code> 値は常に無視される。
* 全ての値が <code>NULL</code> の場合、<code>MAX</code> / <code>MIN</code> は <code>NULL</code> を返す。
<br>
<br>
基本構文を以下に示す。<br>
文字列型への適用例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  REPLACE(str, from_str, to_str)
  -- 辞書順で最初の名前
SELECT MIN(first_name) FROM employees;
-- 辞書順で最後の名前
SELECT MAX(first_name) FROM employees;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>REPLACE</code> 関数は、大文字小文字を区別する。<br>
日付型への適用例を以下に示す。<br>
照合順序が大文字小文字を区別しない場合でも、<code>REPLACE</code> 関数は区別する。<br>
<br>
<syntaxhighlight lang="mysql">
-- 最も古い入社日
SELECT MIN(hire_date) FROM employees;
-- 最も新しい入社日
SELECT MAX(hire_date) FROM employees;
</syntaxhighlight>
<br>
<br>
使用例を以下に示す。<br>
==== GROUP_CONCAT関数 ====
<code>GROUP_CONCAT</code> 関数は、グループ内の値を連結した文字列を生成する集約関数である。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
基本的な使用例を以下に示す。<br>
  SELECT REPLACE('Hello World', 'World', 'MySQL');
<br>
  -- 結果: 'Hello MySQL'
  <syntaxhighlight lang="mysql">
-- 部署ごとの従業員名を連結
  SELECT department_id, GROUP_CONCAT(first_name)
  FROM employees
GROUP BY department_id;
   
   
  SELECT REPLACE('192.168.1.100', '.', '-');
-- セパレータを指定
  -- 結果: '192-168-1-100'
  SELECT department_id, GROUP_CONCAT(first_name SEPARATOR '; ')
  FROM employees
GROUP BY department_id;
   
   
  SELECT REPLACE('apple,apple,orange', 'apple', 'banana');
-- ORDER BY句でソート
  -- 結果: 'banana,banana,orange' (すべての出現を置換)
  SELECT department_id, GROUP_CONCAT(first_name ORDER BY first_name ASC)
  FROM employees
GROUP BY department_id;
   
   
  UPDATE products SET description = REPLACE(description, 'old_term', 'new_term');
  -- 重複を除外
SELECT department_id, GROUP_CONCAT(DISTINCT job_id)
FROM employees
GROUP BY department_id;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
全ての出現が置換される点に注意する必要がある。<br>
オプション句を以下に示す。<br>
* SEPARATOR句
*: 連結時のセパレータを指定する。
*: デフォルトはカンマ (<code>,</code>) である。
*: <code>SEPARATOR ''</code> でセパレータなしの連結も可能である。
* ORDER BY句
*: 連結前に値をソートする。
*: <code>ORDER BY column ASC</code> または <code>ORDER BY column DESC</code> を指定できる。
* DISTINCT
*: 重複する値を除外してから連結する。
<br>
<br>
==== INSERT ====
出力制限に関する注意を以下に示す。<br>
<code>INSERT</code> 関数は、指定位置の文字列を新しい文字列で置換する。<br>
* group_concat_max_len システム変数
*: <code>GROUP_CONCAT</code> の出力最大長を制御する (デフォルト1024バイト)。
*: この制限を超える場合、無警告で切り詰められる。
*: <code>SET SESSION group_concat_max_len = 1000000;</code> で拡張可能である。
* max_allowed_packet
*: 最大パケットサイズも制限要因となる。
<br>
<br>
基本構文を以下に示す。<br>
<code>NULL</code> 値の処理を以下に示す。<br>
* <code>NULL</code> 値は自動的に除外される。
* 全ての値が <code>NULL</code> の場合、<code>GROUP_CONCAT</code> は <code>NULL</code> を返す。
<br>
<br>
  <syntaxhighlight lang="sql">
複数列の連結例を以下に示す。<br>
  INSERT(str, pos, len, newstr)
<br>
  <syntaxhighlight lang="mysql">
  -- 名前と役職を組み合わせて連結
SELECT department_id,
        GROUP_CONCAT(CONCAT(first_name, ' (', job_id, ')') ORDER BY first_name)
FROM employees
GROUP BY department_id;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<center>
出力制限の設定例を以下に示す。<br>
{| class="wikitable"
|+ 引数の意味
|-
! 引数 !! 説明
|-
| 第1引数 || 対象文字列
|-
| 第2引数 || 開始位置 (1始まり)
|-
| 第3引数 || 置換する文字数
|-
| 第4引数 || 挿入する文字列
|}
</center>
<br>
使用例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT INSERT('HelloWorld', 6, 5, 'MySQL');
  -- セッション単位で制限を拡張
  -- 結果: 'HelloMySQL'
  SET SESSION group_concat_max_len = 1000000;
   
   
SELECT INSERT('12345', 3, 0, 'XX');
  -- グローバルに制限を拡張
  -- 結果: '12XX345' (文字を挿入、置換なし)
  SET GLOBAL group_concat_max_len = 1000000;
   
SELECT INSERT('abcdefg', 2, 3, 'XX');
-- 結果: 'aXXefg' (2文字目から3文字を'XX'で置換)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
第3引数に0を指定した場合は、文字列の挿入のみが行われる。<br>
==== JSON_ARRAYAGG / JSON_OBJECTAGG ====
<br><br>
<code>JSON_ARRAYAGG</code> 関数 および <code>JSON_OBJECTAGG</code> 関数は、MySQL 5.7.22以降で使用可能なJSON集約関数である。<br>
 
== 空白除去・パディング ==
==== TRIM / LTRIM / RTRIM ====
<code>TRIM</code> 関数は、文字列の両端から指定文字を除去する。<br>
<code>LTRIM</code> 関数は、文字列の左端から空白を除去する。<br>
<code>RTRIM</code> 関数は、文字列の右端から空白を除去する。<br>
<br>
<br>
基本構文を以下に示す。<br>
===== JSON_ARRAYAGG =====
<code>JSON_ARRAYAGG</code> 関数は、グループ内の値をJSON配列として集約する。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
基本的な使用例を以下に示す。<br>
  TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str)
  <syntaxhighlight lang="mysql">
  LTRIM(str)
  -- 部署ごとの従業員名をJSON配列として集約
  RTRIM(str)
SELECT department_id, JSON_ARRAYAGG(first_name)
FROM employees
GROUP BY department_id;
  -- 結果例
  -- {"department_id": 10, "names": ["John", "Jane", "Bob"]}
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<center>
<code>GROUP_CONCAT</code> との違いを以下に示す。<br>
{| class="wikitable"
* NULL値の扱い
|+ <code>TRIM</code>関数のオプション
*: <code>JSON_ARRAYAGG</code> は、<code>NULL</code> 値も配列要素として含める。
|-
*: <code>GROUP_CONCAT</code> は、<code>NULL</code> 値を無視する。
! オプション !! 説明
* 戻り値の型
|-
*: <code>JSON_ARRAYAGG</code> は、JSON配列型を返す。
| <code>BOTH</code> || 両端から除去 (デフォルト)
*: <code>GROUP_CONCAT</code> は、文字列 (TEXT型) を返す。
|-
| <code>LEADING</code> || 左端から除去
|-
| <code>TRAILING</code> || 右端から除去
|-
| <code>remstr</code> || 除去する文字列 (デフォルトは空白)
|}
</center>
<br>
<br>
使用例を以下に示す。<br>
NULL値を含む例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
SELECT TRIM('  Hello World  ');
  -- NULL値も配列要素として含まれる
  -- 結果: 'Hello World'
  SELECT JSON_ARRAYAGG(commission_pct) FROM employees;
  SELECT LTRIM('  Hello World  ');
-- 結果: 'Hello World  '
   
   
SELECT RTRIM('  Hello World  ');
  -- 結果例: [0.2, 0.3, null, null, 0.15]
  -- 結果: '  Hello World'
SELECT TRIM(BOTH 'x' FROM 'xxxHelloxxx');
-- 結果: 'Hello'
SELECT TRIM(LEADING '0' FROM '000123');
-- 結果: '123'
SELECT TRIM(TRAILING '.' FROM 'example.com...');
-- 結果: 'example.com'
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>TRIM</code> 関数は、ユーザ入力データのクリーニングに有用である。<br>
===== JSON_OBJECTAGG =====
<code>JSON_OBJECTAGG</code> 関数は、キーと値のペアをJSONオブジェクトとして集約する。<br>
<br>
<br>
==== LPAD / RPAD ====
基本的な使用例を以下に示す。<br>
<code>LPAD</code> 関数は、文字列の左側を指定文字でパディングする。<br>
<code>RPAD</code> 関数は、文字列の右側を指定文字でパディングする。<br>
<br>
<br>
基本構文を以下に示す。<br>
  <syntaxhighlight lang="mysql">
<br>
  -- 従業員IDと名前のペアをJSONオブジェクトとして集約
  <syntaxhighlight lang="sql">
SELECT department_id, JSON_OBJECTAGG(employee_id, first_name)
  LPAD(str, len, padstr)
  FROM employees
  RPAD(str, len, padstr)
GROUP BY department_id;
-- 結果例
-- {"department_id": 10, "employees": {"100": "John", "101": "Jane"}}
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<center>
キー重複時の動作を以下に示す。<br>
{| class="wikitable"
* 同じキーが複数回現れる場合、最後の値で上書きされる。
|+ 引数の意味
* どの値が最後になるかは、行の順序に依存する。
|-
* 明示的に <code>ORDER BY</code> 句を使用することで、順序を制御できる。
! 引数 !! 説明
|-
| 第1引数 || 対象文字列
|-
| 第2引数 || 結果の文字列長
|-
| 第3引数 || パディング文字列
|}
</center>
<br>
<br>
使用例を以下に示す。<br>
キー重複の例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT LPAD('123', 6, '0');
-- 同じキーが複数回現れる場合、最後の値で上書き
  -- 結果: '000123'
  SELECT JSON_OBJECTAGG(job_id, first_name)
  FROM employees;
   
   
SELECT RPAD('Hello', 10, '.');
  -- 結果例: {"CLERK": "Sarah", "MANAGER": "Bob"}
  -- 結果: 'Hello.....'
  -- (CLERKやMANAGERが複数いる場合、最後の行の値が使用される)
SELECT LPAD('ID', 10, '-');
-- 結果: '--------ID'
SELECT RPAD('Name', 20, ' ');
  -- 結果: 'Name                ' (空白でパディング)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
元の文字列が指定長より長い場合、切り詰められる。<br>
<br><br>
<br><br>


== 大文字・小文字変換 ==
== GROUP BYとの組み合わせ ==
<code>UPPER</code> 関数 および <code>UCASE</code> 関数は、文字列を大文字に変換する。<br>
==== 基本的な使い方 ====
<code>LOWER</code> 関数 および <code>LCASE</code> 関数は、文字列を小文字に変換する。<br>
<code>GROUP BY</code> 句は、集約関数と組み合わせて使用することにより、データをグループ化して集計を行う。<br>
<br>
基本構文を以下に示す。<br>
<br>
<br>
<syntaxhighlight lang="sql">
基本的な使用例を以下に示す。<br>
UPPER(str)
UCASE(str)
LOWER(str)
LCASE(str)
</syntaxhighlight>
<br>
<br>
<code>UPPER</code> 関数 と <code>UCASE</code> 関数は同一である。<br>
  <syntaxhighlight lang="mysql">
<code>LOWER</code> 関数 と <code>LCASE</code> 関数は同一である。<br>
  -- 部署ごとの従業員数
<br>
  SELECT department_id, COUNT(*)
使用例を以下に示す。<br>
  FROM employees
<br>
GROUP BY department_id;
  <syntaxhighlight lang="sql">
SELECT UPPER('Hello World');
  -- 結果: 'HELLO WORLD'
  SELECT LOWER('Hello World');
  -- 結果: 'hello world'
   
   
  SELECT UCASE('mysql database');
-- 部署ごとの平均給与
  -- 結果: 'MYSQL DATABASE'
  SELECT department_id, AVG(salary)
  FROM employees
GROUP BY department_id;
   
   
  SELECT LCASE('MYSQL DATABASE');
-- 役職ごとの最大給与・最小給与
  -- 結果: 'mysql database'
  SELECT job_id, MAX(salary), MIN(salary)
  FROM employees
GROUP BY job_id;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
バイナリ文字列 (BINARY, VARBINARY, BLOB) に対しては効果がない。<br>
複数列でのグループ化を以下に示す。<br>
<br>
<br>
照合順序によっては、大文字小文字変換が期待通りに動作しない場合がある。<br>
<syntaxhighlight lang="mysql">
<code>utf8mb4_bin</code> 等のバイナリ照合順序では、ASCIIアルファベット以外の変換が行われない場合がある。<br>
-- 部署と役職の組み合わせでグループ化
<br><br>
SELECT department_id, job_id, COUNT(*), AVG(salary)
 
FROM employees
== 文字列検索 ==
GROUP BY department_id, job_id;
==== LOCATE / POSITION ====
</syntaxhighlight>
<code>LOCATE</code> 関数 および <code>POSITION</code> 関数は、部分文字列の位置を検索する。<br>
<br>
<br>
基本構文を以下に示す。<br>
<code>WHERE</code> 句との組み合わせを以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  LOCATE(substr, str)
  -- 給与が5000以上の従業員を対象に部署ごとの平均給与を計算
  LOCATE(substr, str, pos)
SELECT department_id, AVG(salary)
  POSITION(substr IN str)
  FROM employees
WHERE salary >= 5000
  GROUP BY department_id;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<center>
実行順序を以下に示す。<br>
{| class="wikitable"
# WHERE句でフィルタリング (グループ化前)
|+ 引数の意味
# GROUP BY句でグループ化
|-
# 集約関数で集計
! 引数 !! 説明
# HAVING句でフィルタリング (グループ化後)
|-
# ORDER BY句でソート
| 第1引数 || 検索する部分文字列
# LIMIT句で件数制限
|-
| 第2引数 || 対象文字列
|-
| 第3引数 || 検索開始位置 (省略時は1)
|}
</center>
<br>
<br>
戻り値は、部分文字列が最初に出現する位置 (1始まり) である。<br>
==== WITH ROLLUP ====
見つからない場合は0を返す。<br>
<code>WITH ROLLUP</code> 修飾子は、小計と総計を含む追加行を生成する。<br>
<br>
<br>
使用例を以下に示す。<br>
基本的な使用例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
SELECT LOCATE('World', 'Hello World');
  -- 部署ごとの従業員数と総計
  -- 結果: 7
  SELECT department_id, COUNT(*)
  FROM employees
  SELECT POSITION('MySQL' IN 'Hello MySQL Database');
  GROUP BY department_id WITH ROLLUP;
  -- 結果: 7
   
SELECT LOCATE('o', 'Hello World');
-- 結果: 5 (最初の'o'の位置)
   
   
  SELECT LOCATE('o', 'Hello World', 6);
  -- 結果例:
  -- 結果: 8 (6文字目以降で検索)
-- +---------------+----------+
   
-- | department_id | COUNT(*) |
  SELECT LOCATE('xyz', 'Hello World');
  -- +---------------+----------+
  -- 結果: 0 (見つからない)
-- |            10 |        5 |
-- |            20 |        8 |
  -- |            30 |      12 |
  -- |          NULL |      25 |  -- 総計行
  -- +---------------+----------+
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>POSITION</code> 関数は、SQL標準構文である。<br>
複数列でのロールアップを以下に示す。<br>
<br>
<br>
==== INSTR ====
<syntaxhighlight lang="mysql">
<code>INSTR</code> 関数は、<code>LOCATE</code> 関数と同様に部分文字列の位置を検索する。<br>
-- 部署と役職の組み合わせでロールアップ
引数の順序が <code>LOCATE</code> 関数と逆である点が異なる。<br>
SELECT department_id, job_id, COUNT(*)
FROM employees
GROUP BY department_id, job_id WITH ROLLUP;
-- 結果例:
-- +---------------+---------+----------+
-- | department_id | job_id  | COUNT(*) |
-- +---------------+---------+----------+
-- |            10 | CLERK  |        3 |
-- |            10 | MANAGER |        2 |
-- |            10 | NULL    |        5 |  -- 部署10の小計
-- |            20 | CLERK  |        5 |
-- |            20 | ANALYST |        3 |
-- |            20 | NULL    |        8 |  -- 部署20の小計
-- |          NULL | NULL    |      13 |  -- 総計
-- +---------------+---------+----------+
</syntaxhighlight>
<br>
<br>
基本構文を以下に示す。<br>
<code>GROUPING</code> 関数を以下に示す。<br>
<code>GROUPING</code> 関数は、<code>NULL</code> 値が通常のグループ値か、小計行かを区別するために使用する。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  INSTR(str, substr)
  -- GROUPING関数で小計行を識別
SELECT department_id,
        job_id,
        COUNT(*),
        GROUPING(department_id) AS dept_grouping,
        GROUPING(job_id) AS job_grouping
FROM employees
GROUP BY department_id, job_id WITH ROLLUP;
-- GROUPING関数の戻り値:
-- 0 = 通常のグループ値
-- 1 = 小計行 (ROLLUPによって生成されたNULL)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
使用例を以下に示す。<br>
小計行のラベル付け例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT INSTR('Hello World', 'World');
-- 小計行にラベルを付ける
-- 結果: 7
  SELECT IF(GROUPING(department_id), 'Total', department_id) AS department,
        IF(GROUPING(job_id), 'Subtotal', job_id) AS job,
SELECT INSTR('Hello World', 'o');
        COUNT(*)
-- 結果: 5
  FROM employees
   
  GROUP BY department_id, job_id WITH ROLLUP;
  SELECT INSTR('Hello World', 'xyz');
-- 結果: 0
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>INSTR</code> 関数は、検索開始位置を指定できない。<br>
==== ONLY_FULL_GROUP_BY モード ====
検索開始位置を指定する場合は、<code>LOCATE</code> 関数を使用する。<br>
<code>ONLY_FULL_GROUP_BY</code> は、MySQL 8.0のデフォルトSQLモードに含まれる厳格なグループ化ルールである。<br>
<br>
<br>
==== FIND_IN_SET ====
基本的なルールを以下に示す。<br>
<code>FIND_IN_SET</code> 関数は、カンマ区切りリスト内での文字列の位置を検索する。<br>
* <code>SELECT</code> リストの非集約列は、全て <code>GROUP BY</code> 句に含まれる必要がある。
* または、非集約列が <code>GROUP BY</code> 列に関数従属である必要がある。
<br>
<br>
基本構文を以下に示す。<br>
エラーの例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  FIND_IN_SET(str, strlist)
  -- エラー: first_name が GROUP BY に含まれていない
SELECT department_id, first_name, COUNT(*)
FROM employees
GROUP BY department_id;
-- エラーメッセージ:
-- ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause
-- and contains nonaggregated column 'employees.first_name' which is not
-- functionally dependent on columns in GROUP BY clause
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<center>
正しい書き方を以下に示す。<br>
{| class="wikitable"
|+ 引数の意味
|-
! 引数 !! 説明
|-
| 第1引数 || 検索する文字列
|-
| 第2引数 || カンマ区切りの文字列リスト
|}
</center>
<br>
<br>
戻り値は、リスト内での位置 (1始まり) である。<br>
<syntaxhighlight lang="mysql">
見つからない場合は0を返す。<br>
-- 正: first_name を GROUP BY に含める
SELECT department_id, first_name, COUNT(*)
FROM employees
GROUP BY department_id, first_name;
-- 正: first_name を集約関数に含める
SELECT department_id, GROUP_CONCAT(first_name), COUNT(*)
FROM employees
GROUP BY department_id;
</syntaxhighlight>
<br>
<br>
使用例を以下に示す。<br>
<code>ANY_VALUE</code> 関数を以下に示す。<br>
<code>ANY_VALUE</code> 関数は、グループ内の任意の値を返す関数である。<br>
<code>ONLY_FULL_GROUP_BY</code> モードでも、<code>ANY_VALUE</code> を使用することで非集約列を <code>SELECT</code> リストに含めることができる。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
SELECT FIND_IN_SET('b', 'a,b,c,d');
  -- ANY_VALUE関数で回避
  -- 結果: 2
  SELECT department_id, ANY_VALUE(first_name), COUNT(*)
  FROM employees
  SELECT FIND_IN_SET('MySQL', 'Oracle,MySQL,PostgreSQL');
  GROUP BY department_id;
-- 結果: 2
   
  SELECT FIND_IN_SET('x', 'a,b,c,d');
-- 結果: 0
   
   
  SELECT * FROM users WHERE FIND_IN_SET('admin', roles) > 0;
  -- 注意: どの値が返されるかは不定である
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>FIND_IN_SET</code> 関数は、SET型カラムの検索に有用である。<br>
<code>ONLY_FULL_GROUP_BY</code> モードの無効化を以下に示す。<br>
<br>
<br>
==== FIELD / ELT ====
  <syntaxhighlight lang="mysql">
<code>FIELD</code> 関数は、引数リスト内での文字列の位置を返す。<br>
  -- セッション単位で無効化
<code>ELT</code> 関数は、指定位置の要素を返す。<br>
SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', ''));
これらは、互いに補関数の関係にある。<br>
<br>
-- グローバルに無効化
基本構文を以下に示す。<br>
  SET GLOBAL sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', ''));
<br>
  <syntaxhighlight lang="sql">
  FIELD(str, str1, str2, str3, ...)
  ELT(N, str1, str2, str3, ...)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
使用例を以下に示す。<br>
関数従属の例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT FIELD('MySQL', 'Oracle', 'MySQL', 'PostgreSQL');
-- employee_id がプライマリキーの場合、他の列は関数従属
  -- 結果: 2
  SELECT employee_id, first_name, last_name, COUNT(*)
  FROM employees
GROUP BY employee_id;
   
   
  SELECT ELT(2, 'Oracle', 'MySQL', 'PostgreSQL');
  -- employee_id がプライマリキーであるため、first_name と last_name は
-- 結果: 'MySQL'
  -- employee_id に関数従属しており、エラーにならない
SELECT FIELD('xyz', 'a', 'b', 'c');
-- 結果: 0 (見つからない)
SELECT ELT(0, 'a', 'b', 'c');
-- 結果: NULL (位置0は無効)
SELECT ELT(FIELD('b', 'a', 'b', 'c'), 'a', 'b', 'c');
  -- 結果: 'b' (FIELD/ELTの組み合わせ)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<code>FIELD</code> 関数は、カスタムソート順序の実装に使用できる。<br>
<br><br>
<br><br>


== 文字列長 ==
== HAVING句との組み合わせ ==
<code>LENGTH</code> 関数、<code>CHAR_LENGTH</code> 関数、<code>BIT_LENGTH</code> 関数は、文字列の長さを取得する。<br>
<code>HAVING</code> 句は、グループ化後の結果に対してフィルタリングを行う。<br>
これらの関数は、異なる単位で長さを返す。<br>
<br>
基本構文を以下に示す。<br>
<br>
<br>
<syntaxhighlight lang="sql">
<code>WHERE</code> 句 と <code>HAVING</code> 句の違いを以下に示す。<br>
LENGTH(str)
CHAR_LENGTH(str)
CHARACTER_LENGTH(str)
BIT_LENGTH(str)
OCTET_LENGTH(str)
</syntaxhighlight>
<br>
下表に、各関数の戻り値の単位を示す。<br>
<br>
<br>
<center>
<center>
{| class="wikitable"
{| class="wikitable"
|+ 文字列長関数の比較
|+ WHERE句とHAVING句の違い
! 関数 !! 単位 !! 説明
|-
|-
| LENGTH || バイト || バイト長 (マルチバイト文字は複数バイト)
! 項目 !! WHERE句 !! HAVING句
|-
|-
| CHAR_LENGTH || 文字 || 文字数
| フィルタリングのタイミング || グループ化前 || グループ化後
|-
|-
| CHARACTER_LENGTH || 文字 || CHAR_LENGTHの別名
| 条件の対象 || 個々の行 || グループ
|-
|-
| BIT_LENGTH || ビット || ビット長 (LENGTH * 8)
| 集約関数の使用 || 使用できない || 使用できる
|-
| OCTET_LENGTH || バイト || LENGTHの別名
|}
|}
</center>
</center>
<br>
<br>
マルチバイト文字セットでの動作の違いを以下に示す。<br>
基本的な使用例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT LENGTH('Hello');
-- 従業員数が5人以上の部署のみを抽出
  -- 結果: 5 (ASCII文字は1バイト/文字)
  SELECT department_id, COUNT(*)
  FROM employees
GROUP BY department_id
HAVING COUNT(*) >= 5;
   
   
SELECT CHAR_LENGTH('Hello');
  -- 平均給与が7000以上の部署のみを抽出
-- 結果: 5
  SELECT department_id, AVG(salary)
  FROM employees
SELECT LENGTH('こんにちは');
  GROUP BY department_id
-- 結果: 15 (UTF-8では日本語は3バイト/文字)
  HAVING AVG(salary) >= 7000;
SELECT CHAR_LENGTH('こんにちは');
-- 結果: 5 (文字数)
SELECT BIT_LENGTH('Hello');
  -- 結果: 40 (5バイト * 8ビット)
  SELECT LENGTH('MySQL') = CHAR_LENGTH('MySQL');
  -- 結果: 1 (ASCIIのみの場合は一致)
   
  SELECT LENGTH('データベース') = CHAR_LENGTH('データベース');
-- 結果: 0 (マルチバイト文字の場合は不一致)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<u>VARCHAR型の文字数制限は、バイト数ではなく文字数で指定される。</u><br>
<code>WHERE</code> 句 と <code>HAVING</code> 句の組み合わせを以下に示す。<br>
例えば、VARCHAR(100) は、100文字まで格納できる。<br>
UTF-8エンコーディングでは、日本語100文字は最大300バイトとなる。<br>
<br>
WHERE句での文字列長検査には、<code>CHAR_LENGTH</code> 関数を使用することを推奨する。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT * FROM articles WHERE CHAR_LENGTH(title) > 50;
-- 給与が5000以上の従業員を対象に、
-- 従業員数が3人以上の部署の平均給与を計算
  SELECT department_id, COUNT(*), AVG(salary)
FROM employees
WHERE salary >= 5000
GROUP BY department_id
HAVING COUNT(*) >= 3;
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
== その他の文字列関数 ==
MySQLには、その他にも多数の文字列関数が提供されている。<br>
<br>
<br>
下表に、主要な関数を示す。<br>
複数の条件を指定する例を以下に示す。<br>
<br>
<br>
<center>
<syntaxhighlight lang="mysql">
{| class="wikitable"
-- 従業員数が5人以上、かつ平均給与が7000以上の部署
|+ その他の文字列関数
SELECT department_id, COUNT(*), AVG(salary)
! 関数 !! 説明 !! 使用例
FROM employees
|-
GROUP BY department_id
| REVERSE || 文字列を反転 || REVERSE('Hello') → 'olleH'
HAVING COUNT(*) >= 5 AND AVG(salary) >= 7000;
|-
</syntaxhighlight>
| REPEAT || 文字列を繰り返し || REPEAT('X', 5) → 'XXXXX'
|-
| SPACE || 指定数の空白を生成 || SPACE(3) → '  '
|-
| FORMAT || 数値を千の位区切りでフォーマット || FORMAT(1234567.89, 2) → '1,234,567.89'
|-
| QUOTE || SQL文字列エスケープ || QUOTE("It's") → 'It\'s'
|-
| SOUNDEX || サウンデックス (音韻表現) || SOUNDEX('Smith') → 'S530'
|-
| STRCMP || 文字列比較 (0=等しい, -1=小, 1=大) || STRCMP('a', 'b') → -1
|}
</center>
<br>
<br>
===== REVERSE =====
エイリアスの使用を以下に示す。<br>
<code>REVERSE</code> 関数は、文字列を反転する。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT REVERSE('Hello World');
-- HAVING句でエイリアスを使用
  -- 結果: 'dlroW olleH'
  SELECT department_id, COUNT(*) AS emp_count, AVG(salary) AS avg_salary
   
  FROM employees
  SELECT REVERSE('12345');
  GROUP BY department_id
-- 結果: '54321'
  HAVING emp_count >= 5 AND avg_salary >= 7000;
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
== ウインドウ関数としての使用 ==
MySQL 8.0以降では、集約関数を <code>OVER()</code> 句と組み合わせてウインドウ関数として使用できる。<br>
ウインドウ関数として使用した場合、行を削減せず各行が保持される。<br>
<br>
<br>
===== REPEAT =====
基本的な使用例を以下に示す。<br>
<code>REPEAT</code> 関数は、文字列を指定回数繰り返す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT REPEAT('*', 10);
-- 各従業員の給与と全体の平均給与を表示
  -- 結果: '**********'
  SELECT employee_id,
        first_name,
        salary,
        AVG(salary) OVER() AS avg_salary
  FROM employees;
   
   
  SELECT REPEAT('MySQL ', 3);
  -- 結果例:
  -- 結果: 'MySQL MySQL MySQL '
-- +-------------+------------+--------+------------+
  -- | employee_id | first_name | salary | avg_salary |
-- +-------------+------------+--------+------------+
-- |        100 | John      |  8000 |      6500 |
-- |        101 | Jane      |  7000 |      6500 |
-- |        102 | Bob        |  5000 |      6500 |
-- +-------------+------------+--------+------------+
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== SPACE =====
<code>PARTITION BY</code> 句を以下に示す。<br>
<code>SPACE</code> 関数は、指定数の空白文字を生成する。<br>
<code>PARTITION BY</code> 句は、ウインドウを部分的に分割して、部分ごとの集約を行う。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT CONCAT('Hello', SPACE(5), 'World');
-- 部署ごとの平均給与を各行に表示
  -- 結果: 'Hello    World'
  SELECT employee_id,
        first_name,
        department_id,
        salary,
        AVG(salary) OVER(PARTITION BY department_id) AS dept_avg_salary
FROM employees;
  -- 結果例:
-- +-------------+------------+---------------+--------+------------------+
-- | employee_id | first_name | department_id | salary | dept_avg_salary  |
-- +-------------+------------+---------------+--------+------------------+
-- |        100 | John      |            10 |  8000 |            7000 |
-- |        101 | Jane      |            10 |  6000 |            7000 |
-- |        102 | Bob        |            20 |  5000 |            5500 |
-- |        103 | Alice      |            20 |  6000 |            5500 |
-- +-------------+------------+---------------+--------+------------------+
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== FORMAT =====
<code>ORDER BY</code> 句を以下に示す。<br>
<code>FORMAT</code> 関数は、数値を千の位区切りでフォーマットする。<br>
<code>ORDER BY</code> 句は、ウインドウ内の行順序を指定する。<br>
これにより、累積合計や移動平均などの計算が可能になる。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT FORMAT(1234567.89, 2);
-- 部署ごとの給与の累積合計
  -- 結果: '1,234,567.89'
  SELECT employee_id,
        first_name,
        department_id,
        salary,
        SUM(salary) OVER(PARTITION BY department_id ORDER BY employee_id) AS cumulative_salary
  FROM employees;
   
   
  SELECT FORMAT(1234567.89, 0);
  -- 結果例:
  -- 結果: '1,234,568' (小数点以下四捨五入)
-- +-------------+------------+---------------+--------+-------------------+
   
-- | employee_id | first_name | department_id | salary | cumulative_salary |
  SELECT FORMAT(1234567.89, 2, 'de_DE');
-- +-------------+------------+---------------+--------+-------------------+
  -- 結果: '1.234.567,89' (ドイツ語ロケール)
-- |        100 | John      |            10 |  8000 |              8000 |
  -- |        101 | Jane      |            10 |  6000 |            14000 |
  -- |        102 | Bob        |            20 |  5000 |              5000 |
  -- |        103 | Alice      |            20 |  6000 |            11000 |
  -- +-------------+------------+---------------+--------+-------------------+
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== QUOTE =====
フレーム指定を以下に示す。<br>
<code>QUOTE</code> 関数は、SQL文字列としてエスケープする。<br>
<code>ROWS</code> 関数 または <code>RANGE</code> 句を使用して、ウインドウ内の特定の行範囲に対する集約を行うことができる。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT QUOTE("It's a test");
  -- 現在行と直前の2行 (合計3行) の移動平均
  -- 結果: 'It\'s a test'
  SELECT employee_id,
        first_name,
SELECT QUOTE('He said "Hello"');
        salary,
  -- 結果: 'He said \"Hello\"'
        AVG(salary) OVER(ORDER BY employee_id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
  FROM employees;
   
   
  SELECT QUOTE(NULL);
-- 現在行と前後1行 (合計3行) の移動平均
  -- 結果: NULL
  SELECT employee_id,
        first_name,
        salary,
        AVG(salary) OVER(ORDER BY employee_id ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS moving_avg
  FROM employees;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== SOUNDEX =====
下表に、フレーム指定のキーワードを示す。<br>
<code>SOUNDEX</code> 関数は、英語の音韻表現を生成する。<br>
<br>
類似した発音の単語を検索する場合に使用する。<br>
<center>
{| class="wikitable"
|+ フレーム指定のキーワード
|-
! キーワード !! 説明
|-
| ROWS || 物理的な行数でフレームを指定する
|-
| RANGE || 値の範囲でフレームを指定する
|-
| UNBOUNDED PRECEDING || パーティションの最初の行から
|-
| UNBOUNDED FOLLOWING || パーティションの最後の行まで
|-
| CURRENT ROW || 現在の行
|-
| n PRECEDING || 現在行からn行前
|-
| n FOLLOWING || 現在行からn行後
|}
</center>
<br>
<br>
<syntaxhighlight lang="sql">
* 累積合計の例
  SELECT SOUNDEX('Smith');
*: <syntaxhighlight lang="mysql">
  -- 結果: 'S530'
  -- 部署ごとの給与の累積合計 (明示的なフレーム指定)
  SELECT employee_id,
SELECT SOUNDEX('Smythe');
        first_name,
-- 結果: 'S530' (Smithと同じ音韻コード)
        department_id,
   
        salary,
SELECT * FROM users WHERE SOUNDEX(name) = SOUNDEX('Jon');
        SUM(salary) OVER(
-- 'John', 'Jon', 'Jonn' 等を検索
          PARTITION BY department_id
          ORDER BY employee_id
          ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS cumulative_salary
  FROM employees;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
*: <br>
===== STRCMP =====
* 移動平均の例
<code>STRCMP</code> 関数は、2つの文字列を比較する。<br>
*: <syntaxhighlight lang="mysql">
<br>
  -- 直近3件の給与の移動平均
<syntaxhighlight lang="sql">
  SELECT employee_id,
SELECT STRCMP('abc', 'abc');
        first_name,
  -- 結果: 0 (等しい)
        salary,
   
        AVG(salary) OVER(
SELECT STRCMP('abc', 'def');
          ORDER BY employee_id
-- 結果: -1 (第1引数が小)
          ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
        ) AS moving_avg_3
SELECT STRCMP('def', 'abc');
  FROM employees;
  -- 結果: 1 (第1引数が大)
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
== エンコード関連 ==
MySQLは、文字列とバイナリデータのエンコード変換関数を提供している。<br>
<br>
<br>
下表に、主要なエンコード関連関数を示す。<br>
下表に、サポートされる集約関数を示す。<br>
MySQL 8.0.12以降では、以下に示す集約関数がウインドウ関数としてサポートされている。<br>
<br>
<br>
<center>
<center>
{| class="wikitable"
{| class="wikitable"
|+ エンコード関連関数
|+ ウインドウ関数としてサポートされる集約関数 (MySQL 8.0.12以降)
! 関数 !! 説明 !! 使用例
|-
|-
| HEX || 16進数変換 || HEX('MySQL') → '4D7953514C'
! 集約関数
|-
|-
| UNHEX || 16進数→バイナリ文字列 || UNHEX('4D7953514C') → 'MySQL'
| COUNT
|-
|-
| ASCII || 最初の文字のASCIIコード || ASCII('A') → 65
| SUM
|-
|-
| ORD || 最初の文字のUnicodeコードポイント || ORD('あ') → 12354
| AVG
|-
|-
| CHAR || 整数コード→文字列 || CHAR(65, 66, 67) → 'ABC'
| MAX
|-
|-
| BIN || 2進数変換 || BIN(12) → '1100'
| MIN
|-
|-
| OCT || 8進数変換 || OCT(12) → '14'
| GROUP_CONCAT
|-
|-
| TO_BASE64 || Base64エンコード || TO_BASE64('MySQL') → 'TXlTUUw='
| JSON_ARRAYAGG
|-
|-
| FROM_BASE64 || Base64デコード || FROM_BASE64('TXlTUUw=') → 'MySQL'
| JSON_OBJECTAGG
|}
|}
</center>
</center>
<br><br>
== パフォーマンス ==
==== インデックスの活用 ====
集約関数のパフォーマンスは、インデックスの有無に大きく影響される。<br>
<br>
<br>
===== HEX / UNHEX =====
<code>MAX</code> / <code>MIN</code> 関数とインデックスの関係を以下に示す。<br>
<code>HEX</code> 関数は、文字列またはバイナリデータを16進数表現に変換する。<br>
* インデックスが存在する列に対して <code>MAX</code> / <code>MIN</code> を使用する場合、インデックスを活用した高速な検索が可能である。
<code>UNHEX</code> 関数は、16進数表現をバイナリデータに変換する。<br>
* これは、<code>ORDER BY column DESC LIMIT 1</code> (<code>MAX</code>) や <code>ORDER BY column ASC LIMIT 1</code> (<code>MIN</code>) と同等の最適化が行われるためである。
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT HEX('MySQL');
-- インデックスが存在する場合、高速に実行される
-- 結果: '4D7953514C'
  SELECT MAX(employee_id) FROM employees;
   
   
SELECT UNHEX('4D7953514C');
  -- EXPLAIN で確認
  -- 結果: 'MySQL'
  EXPLAIN SELECT MAX(employee_id) FROM employees;
  -- type: index (インデックススキャン)
  SELECT HEX(255);
  -- 結果: 'FF'
SELECT HEX('あ');
-- 結果: 'E38182' (UTF-8エンコーディング)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== ASCII / ORD =====
<code>COUNT</code> 関数とインデックスの関係を以下に示す。<br>
<code>ASCII</code> 関数は、文字列の最初の文字のASCIIコードを返す。<br>
* InnoDBでは、<code>COUNT(*)</code> は最小の利用可能なセカンダリインデックスをスキャンする。
<code>ORD</code> 関数は、文字列の最初の文字のUNICODEコードポイントを返す。<br>
* プライマリキーより小さいセカンダリインデックスが存在する場合、そちらを優先的に使用する。
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight lang="mysql">
  SELECT ASCII('A');
-- セカンダリインデックスが存在する場合、そちらをスキャン
-- 結果: 65
  SELECT COUNT(*) FROM employees;
   
   
SELECT ASCII('MySQL');
  -- EXPLAIN で確認
  -- 結果: 77 (最初の文字 'M')
  EXPLAIN SELECT COUNT(*) FROM employees;
  -- key: index_name (セカンダリインデックス)
  SELECT ORD('あ');
  -- 結果: 12354 (Unicodeコードポイント U+3042)
SELECT ORD('MySQL');
-- 結果: 77
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>ORD</code> 関数は、マルチバイト文字に対応している。<br>
<code>GROUP BY</code> とインデックスの関係を以下に示す。<br>
* <code>GROUP BY</code> 列にインデックスが存在する場合、ソート処理が不要になる場合がある。
* <code>EXPLAIN</code> で <code>Using temporary; Using filesort</code> が表示されない場合、インデックスが活用されている。
<br>
<br>
===== CHAR =====
  <syntaxhighlight lang="mysql">
<code>CHAR</code> 関数は、整数コードから文字列を生成する。<br>
  -- department_idにインデックスが存在する場合、高速に実行される
<br>
  SELECT department_id, COUNT(*)
  <syntaxhighlight lang="sql">
  FROM employees
SELECT CHAR(65, 66, 67);
GROUP BY department_id;
  -- 結果: 'ABC'
  SELECT CHAR(77, 121, 83, 81, 76);
  -- 結果: 'MySQL'
   
   
  SELECT CHAR(12354 USING utf8mb4);
  -- EXPLAINで確認
  -- 結果: 'あ' (Unicodeコードポイント U+3042)
EXPLAIN SELECT department_id, COUNT(*) FROM employees GROUP BY department_id;
  -- Extra: Using index (インデックスのみでクエリを実行)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
<code>USING</code> 句で文字セットを指定できる。<br>
==== 大量データの集計 ====
大量のデータを集計する場合、パフォーマンスに注意が必要である。<br>
<br>
<br>
===== BIN / OCT =====
一時テーブルの使用を以下に示す。<br>
<code>BIN</code> 関数は、整数を2進数表現に変換する。<br>
* <code>GROUP BY</code> でグループ化を行う場合、内部的に一時テーブル (TEMPORARY TABLE) が使用される可能性がある。
<code>OCT</code> 関数は、整数を8進数表現に変換する。<br>
* 一時テーブルのサイズが <code>tmp_table_size</code> および <code>max_heap_table_size</code> を超える場合、ディスク上の一時テーブルが使用される。
* ディスク上の一時テーブルは、メモリ上の一時テーブルより遅い。
<br>
<br>
  <syntaxhighlight lang="sql">
  <syntaxhighlight sql">
SELECT BIN(12);
  -- 一時テーブルのサイズ制限を確認
  -- 結果: '1100'
  SHOW VARIABLES LIKE 'tmp_table_size';
  SHOW VARIABLES LIKE 'max_heap_table_size';
SELECT BIN(255);
  -- 結果: '11111111'
SELECT OCT(12);
  -- 結果: '14'
   
   
  SELECT OCT(255);
  -- EXPLAIN で一時テーブルの使用を確認
  -- 結果: '377'
EXPLAIN SELECT department_id, COUNT(*) FROM employees GROUP BY department_id;
  -- Extra: Using temporary (一時テーブルを使用)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== TO_BASE64 / FROM_BASE64 =====
パフォーマンス改善の方法を以下に示す。<br>
<code>TO_BASE64</code> 関数は、文字列をBase64エンコードする。<br>
* インデックスの追加
<code>FROM_BASE64</code> 関数は、Base64文字列をデコードする。<br>
*: <code>GROUP BY</code> 列にインデックスを追加する。
* 一時テーブルサイズの拡張
*: <code>tmp_table_size</code> および <code>max_heap_table_size</code> を増やす。
* パーティショニング
*: 大量データをパーティション分割して、集計対象を絞る。
<br>
<br>
これらの関数は、MySQL 5.6以降で使用可能である。<br>
==== GROUP_CONCATの制限 ====
<code>GROUP_CONCAT</code> は、出力制限に注意が必要である。<br>
<br>
<br>
<syntaxhighlight lang="sql">
出力制限を以下に示す。<br>
SELECT TO_BASE64('MySQL');
* group_concat_max_len (デフォルト1024バイト)
-- 結果: 'TXlTUUw='
*: <code>GROUP_CONCAT</code> の出力最大長を制御する。
*: この制限を超える場合、無警告で切り詰められる。
SELECT FROM_BASE64('TXlTUUw=');
* max_allowed_packet
-- 結果: 'MySQL'
*: 最大パケットサイズも制限要因となる。
SELECT TO_BASE64('こんにちは');
-- 結果: '44GT44KT44Gr44Gh44Gv'
SELECT FROM_BASE64('44GT44KT44Gr44Gh44Gv');
-- 結果: 'こんにちは'
</syntaxhighlight>
<br>
<br>
Base64エンコーディングは、バイナリデータをテキスト形式で格納する場合に有用である。<br>
切り詰め時の挙動を以下に示す。<br>
<br><br>
* 制限を超えた場合、無警告で切り詰められる。
 
* 警告 (WARNING) は生成されない。
== 照合順序との関係 ==
* <code>SHOW WARNINGS;</code> でも確認できない。
文字列関数の動作は、照合順序 (Collation) に依存する場合がある。<br>
照合順序は、文字列比較、ソート、大文字小文字の区別を制御する。<br>
<br>
MySQL 8.0のデフォルト照合順序は、<u>utf8mb4_0900_ai_ci</u> である。<br>
<br>
<br>
照合順序の種類を以下に示す。<br>
推奨される対策を以下に示す。<br>
* セッション単位で <code>group_concat_max_len</code> を拡張する。
* または、グローバルに <code>group_concat_max_len</code> を拡張する。
<br>
<br>
<center>
  <syntaxhighlight lang="mysql">
{| class="wikitable"
  -- セッション単位で拡張
|+ 主要な照合順序
SET SESSION group_concat_max_len = 1000000;
! 照合順序 !! 特性 !! 説明
|-
| utf8mb4_general_ci || 大小文字区別なし、高速 || MySQL 5.7以前のデフォルト
|-
| utf8mb4_unicode_ci || 大小文字区別なし、Unicode準拠 || 多言語対応
|-
| utf8mb4_0900_ai_ci || アクセント非依存、大小文字区別なし || MySQL 8.0デフォルト
|-
| utf8mb4_bin || バイナリ比較 || 大文字小文字、アクセントを区別
|}
</center>
<br>
===== COLLATE句での照合順序指定 =====
<code>COLLATE</code> 句により、クエリレベルで照合順序を指定できる。<br>
<br>
  <syntaxhighlight lang="sql">
  SELECT 'abc' = 'ABC';
-- 結果: 1 (デフォルトでは大文字小文字区別なし)
   
   
SELECT 'abc' = 'ABC' COLLATE utf8mb4_bin;
  -- グローバルに拡張
-- 結果: 0 (バイナリ比較では区別)
  SET GLOBAL group_concat_max_len = 1000000;
SELECT 'Müller' = 'Mueller' COLLATE utf8mb4_0900_ai_ci;
  -- 結果: 1 (アクセント非依存)
  SELECT 'Müller' = 'Mueller' COLLATE utf8mb4_bin;
-- 結果: 0 (バイナリ比較では区別)
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== BINARY演算子 =====
==== DISTINCT性能 ====
<code>BINARY</code> 演算子により、バイナリ比較を強制できる。<br>
<code>DISTINCT</code> を使用する集約関数は、追加の処理が必要なためパフォーマンスが低下する傾向がある。<br>
<br>
<br>
<syntaxhighlight lang="sql">
パフォーマンス特性を以下に示す。<br>
SELECT 'abc' = 'ABC';
* COUNT(DISTINCT column)
-- 結果: 1
*: <code>DISTINCT</code> 処理のため、<code>COUNT(column)</code> より遅い傾向がある。
*: 内部的に一時テーブルを使用する。
SELECT BINARY 'abc' = 'ABC';
* SUM(DISTINCT column) / AVG(DISTINCT column)
-- 結果: 0
*: 同様に、<code>DISTINCT</code> 処理のため遅い傾向がある。
SELECT 'abc' = BINARY 'ABC';
-- 結果: 0
SELECT LOCATE('WORLD', 'Hello World');
-- 結果: 7 (大文字小文字区別なし)
SELECT LOCATE(BINARY 'WORLD', 'Hello World');
-- 結果: 0 (バイナリ比較で不一致)
</syntaxhighlight>
<br>
<br>
===== 文字セットと照合順序の推奨 =====
推奨される対策を以下に示す。<br>
MySQLでは、<u>utf8mb4</u> 文字セットの使用を強く推奨する。<br>
* インデックスの追加
<u>utf8</u> 文字セットは、最大3バイト/文字であり、絵文字等の4バイト文字を格納できない。<br>
*: <code>DISTINCT</code> 対象列にインデックスを追加する。
<u>utf8mb4</u> 文字セットは、最大4バイト/文字であり、全てのUNICODE文字を格納できる。<br>
* サブクエリの使用
*: 大量データの場合、サブクエリで事前に <code>DISTINCT</code> を行うことで高速化できる場合がある。
<br>
<br>
* テーブル作成時の推奨設定
<syntaxhighlight lang="mysql">
*: <syntaxhighlight lang="sql">
  -- サブクエリで事前にDISTINCTを行う
  CREATE TABLE users (
SELECT COUNT(*) FROM (
     id INT PRIMARY KEY,
     SELECT DISTINCT department_id FROM employees
    name VARCHAR(100)
  ) AS distinct_depts;
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
</syntaxhighlight>
*: <br>
* データベース全体のデフォルト文字セットを設定する場合
*: <syntaxhighlight lang="sql">
CREATE DATABASE mydb
    DEFAULT CHARACTER SET utf8mb4
    DEFAULT COLLATE utf8mb4_unicode_ci;
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
== 文字列関数を使用する場合の注意 ==
==== 文字列関数とインデックス ====
WHERE句で文字列関数を使用すると、インデックスが使用されない場合が多い。<br>
これにより、クエリのパフォーマンスが大幅に低下する可能性がある。<br>
<br>
<br>
===== インデックスが使用されない例 =====
==== WITH ROLLUPの影響 ====
<syntaxhighlight lang="sql">
<code>WITH ROLLUP</code> は、追加の処理が必要なためパフォーマンスに影響する。<br>
-- インデックスが使用されない
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
-- インデックスが使用されない
SELECT * FROM articles WHERE SUBSTRING(title, 1, 5) = 'MySQL';
</syntaxhighlight>
<br>
<br>
===== インデックスを使用する改善例 =====
パフォーマンス特性を以下に示す。<br>
<syntaxhighlight lang="sql">
* <code>WITH ROLLUP</code> は、小計行を生成するため、追加の処理が必要である。
-- インデックスが使用される
* 複数列でロールアップする場合、さらに処理が増加する。
SELECT * FROM users WHERE name = 'John';
-- インデックスが使用される (前方一致)
SELECT * FROM articles WHERE title LIKE 'MySQL%';
</syntaxhighlight>
<br>
<br>
===== 関数ベースインデックス (Generated Column) を使用する例 =====
推奨される対策を以下に示す。<br>
<syntaxhighlight lang="sql">
* インデックスの追加
-- Generated Columnを作成
*: <code>GROUP BY</code> 列にインデックスを追加する。
ALTER TABLE users ADD COLUMN name_upper VARCHAR(100) AS (UPPER(name)) STORED;
* UNION ALLの使用
*: 大量データの場合、<code>UNION ALL</code> で小計行を個別に計算する方が高速な場合がある。
-- インデックスを作成
CREATE INDEX idx_name_upper ON users(name_upper);
-- インデックスが使用される
SELECT * FROM users WHERE name_upper = 'JOHN';
</syntaxhighlight>
<br>
<br>
MySQL 8.0以降では、関数インデックスが直接サポートされている。<br>
  <syntaxhighlight lang="mysql">
  <syntaxhighlight lang="sql">
  -- WITH ROLLUP の代わりに UNION ALL を使用
  -- MySQL 8.0以降
  SELECT department_id, COUNT(*) FROM employees GROUP BY department_id
  CREATE INDEX idx_upper_name ON users((UPPER(name)));
  UNION ALL
   
  SELECT NULL, COUNT(*) FROM employees;
-- インデックスが使用される
  SELECT * FROM users WHERE UPPER(name) = 'JOHN';
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
== バージョン別の機能差異 ==
MySQLのバージョンによって、集約関数の機能に差異がある。<br>
<br>
<br>
==== 大きなテキストデータ ====
下表に、MySQL 5.7とMySQL 8.0の主要な違いを示す。<br>
TEXT型およびBLOB型での文字列関数使用は、パフォーマンスが低下する傾向がある。<br>
<br>
理由を以下に示す。<br>
* TEXT / BLOB型は、オフページ格納される場合がある。
*: データが別の場所に格納され、アクセスが遅くなる。
* インデックスの制限
*: TEXT/BLOB型の全体にインデックスを作成できない。
* メモリ使用量
*: 一時テーブルがディスクに作成される場合がある。
<br>
大きなテキストデータの処理には、以下に示す対策を推奨する。<br>
<br>
<br>
<center>
<center>
{| class="wikitable"
{| class="wikitable"
|+ 大きなテキストデータの処理に対する推奨対策
|+ MySQL 5.7 vs MySQL 8.0
|-
! 機能 !! MySQL 5.7 !! MySQL 8.0
! 対策 !! 説明
|-
| VARCHAR型を優先 || VARCHAR型は、最大65,535バイトまで格納可能<br>VARCHAR(5000) 等で十分な場合が多い。
|-
| 全文検索インデックス (FULLTEXT) を使用 || テキスト検索にはFULLTEXTインデックスが効率的である。
|-
|-
| アプリケーションレイヤでの処理 || 複雑な文字列処理は、アプリケーション側で実施する。
| JSON_ARRAYAGG / JSON_OBJECTAGG || 5.7.22以降でサポート || サポート
|}
</center>
<br>
===== マルチバイト文字の考慮 =====
マルチバイト文字セット (utf8mb4) では、以下に示すに注意する。<br>
<br>
<center>
{| class="wikitable"
|+ 文字列処理に関する注意事項
|-
|-
! 項目 !! 説明
| ウインドウ関数 (OVER句) || 未サポート || サポート (8.0以降)
|-
|-
| <code>LENGTH</code> 関数 と <code>CHAR_LENGTH</code> 関数の違い || <code>LENGTH</code> 関数はバイト長、<code>CHAR_LENGTH</code> 関数は文字数<br>日本語等では、<code>LENGTH</code>関数の戻り値は文字数の3倍
| GROUPING関数 || 未サポート || サポート (8.0.12以降)
|-
|-
| VARCHAR型の長さ制限 || VARCHAR(100) は、100文字 (最大400バイト)<br>バイト長制限ではなく文字数制限
| ONLY_FULL_GROUP_BY || デフォルトで有効 || デフォルトで有効
|-
|-
| インデックスキーのサイズ制限 || InnoDBのインデックスキー最大長は767バイト (デフォルト)<br>VARCHAR(255) のutf8mb4カラムは、最大1020バイトとなり、インデックス作成に失敗する可能性<br><code>innodb_large_prefix=ON</code> (MySQL 5.7以降デフォルト) で3072バイトまで拡張可能
| COUNT関数の戻り値 || BIGINT UNSIGNED || BIGINT
|}
|}
</center>
</center>
<br>
<br>
インデックス作成時の注意例を以下に示す。<br>
MySQL 5.7での注意点を以下に示す。<br>
* ウインドウ関数は使用できない。
*: <code>OVER()</code> 句はサポートされていない。
*: 代替として、サブクエリや自己結合を使用する必要がある。
* GROUPING関数は使用できない。
*: <code>WITH ROLLUP</code> で小計行を識別するには、<code>NULL</code> チェックのみを使用する。
<br>
<br>
<syntaxhighlight lang="sql">
MySQL 8.0での新機能を以下に示す。<br>
-- VARCHAR(255) utf8mb4カラムのインデックス作成
* ウインドウ関数
CREATE INDEX idx_title ON articles(title);
*: 集約関数を <code>OVER()</code> 句と組み合わせて使用できる。
-- ERROR: Specified key was too long; max key length is 767 bytes
*: 累積合計、移動平均等の計算が可能になる。
* GROUPING関数
-- 解決策1: プレフィックスインデックス
*: <code>WITH ROLLUP</code> で小計行を識別できる。
CREATE INDEX idx_title ON articles(title(191));
* 改善された集約関数のパフォーマンス
-- 191文字 * 4バイト = 764バイト (767バイト以下)
*: インデックスの活用が改善されている。
-- 解決策2: innodb_large_prefix有効化 (MySQL 5.7以降デフォルト)
SET GLOBAL innodb_large_prefix = 1;
SET GLOBAL innodb_file_format = 'Barracuda';
-- テーブルにROW_FORMAT=DYNAMICを指定
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE INDEX idx_title ON articles(title);
-- 成功 (最大3072バイトまで許容)
</syntaxhighlight>
<br>
===== NULL処理 =====
多くの文字列関数は、<code>NULL</code> を特別に扱う。<br>
<br>
<code>NULL</code> 処理の例を以下に示す。<br>
<br>
<br>
  <syntaxhighlight lang="sql">
MySQL 5.7でウインドウ関数を代替する例を以下に示す。<br>
SELECT CONCAT('Hello', NULL, 'World');
  <syntaxhighlight lang="mysql">
  -- 結果: NULL (いずれかの引数がNULLなら結果はNULL)
  -- MySQL 8.0 (ウインドウ関数)
  SELECT employee_id,
  SELECT CONCAT_WS(',', 'A', NULL, 'B');
        salary,
-- 結果: 'A,B' (NULLはスキップ)
        AVG(salary) OVER() AS avg_salary
  FROM employees;
SELECT LENGTH(NULL);
  -- 結果: NULL
SELECT UPPER(NULL);
-- 結果: NULL
   
   
  SELECT COALESCE(name, '(未設定)') FROM users;
-- MySQL 5.7 (サブクエリで代替)
-- NULLを'(未設定)'で置換
  SELECT e.employee_id,
        e.salary,
        (SELECT AVG(salary) FROM employees) AS avg_salary
FROM employees e;
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<u><code>NULL</code> を扱う場合は、<code>COALESCE</code> 関数 や <code>IFNULL</code> 関数を併用することを推奨する。</u><br>
<br><br>
<br><br>


1,087行目: 1,014行目:
{{#seo:
{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,MySQL,Database,SQL,String Functions,CONCAT,SUBSTRING,REPLACE,TRIM,UPPER,LOWER,LENGTH,CHAR_LENGTH,文字列関数,データベース,電気回路,電子回路,基板,プリント基板
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,MySQL,SQL,Database,集約関数,Aggregate Functions,COUNT,SUM,AVG,MAX,MIN,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,GROUP BY,HAVING,WITH ROLLUP,ONLY_FULL_GROUP_BY,ウインドウ関数,Window Functions,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - MySQLの文字列関数に関する包括的なリファレンス | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|description={{PAGENAME}} - MySQLの集約関数の使い方とパフォーマンス最適化 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}
}}