文字列関数(連結・文字数・大文字小文字)の使い方
shain社員
| shain_id | name |
|---|---|
| 1 | 鈴木 |
| 2 | 佐藤 |
| 3 | 田中 |
| 4 | 中村 |
| 5 | 小林 |
| 6 | 加藤 |
| … | |
CONCAT で作った列を足す作った結果
| shain_id | name | keishou |
|---|---|---|
| 1 | 鈴木 | 鈴木 さん |
| 2 | 佐藤 | 佐藤 さん |
| 3 | 田中 | 田中 さん |
| 4 | 中村 | 中村 さん |
| 5 | 小林 | 小林 さん |
| 6 | 加藤 | 加藤 さん |
| … | ||
CONCAT(name, ' さん') AS keishou
- 青い列= 式から作った列。元の表には無い列です
- 社員 18 行 → 作った結果 18 行(行数は変わりません。減らすのは WHERE の仕事です)
行は 1 行も減っていません。文字列関数は値を作るだけで、行の数には触れません。
文字列関数は「行を絞る」のではなく「値を作る」道具です。 ここでは使用頻度の高い連結・文字数・大文字小文字を扱います。いちばん事故が多いのは連結で、NULL が 1 つ混ざるだけで結果が丸ごと消える書き方があります。
この書き方が使えない製品があります そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / Oracle Database
- SQL Server では使えません(|| による文字列連結・LENGTH(文字数))
未確認: IBM Db2
このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。
01連結には 2 つの書き方がある
結論: || 演算子と CONCAT() 関数のどちらでも文字列をつなげます。
a || b || c… 標準 SQL の演算子。短く書けるCONCAT(a, b, c)… 関数。引数をいくつでも並べられる
見た目が違うだけに見えますが、NULL が混ざったときの動きが正反対です(次の節)。ここを知らずに使い分けると、本番でだけ空欄が出ます。
⚠️ || は SQL Server では使えません(+ を使います)。移植の予定があるなら CONCAT() のほうが安全です。使えない製品がある例には自動で注記が出ます。
SELECT s.name,元の列b.busho_name,つなげたい相手の列s.name || '/' || b.busho_nameつないで新しい値を作るAS shozoku作った列に名前を付けるFROM shain sどの表を見るかLEFT JOIN busho b ON b.busho_id = s.busho_id部署名を引いてくる
つなぐ相手は同じ表の列でも、結合した別の表の列でも構いません。文字列以外(数値など)も多くの製品では自動で文字列に直されます。
SELECT name || ' さん' AS keishou
FROM shain
ORDER BY shain_id;| keishou |
|---|
| 鈴木 さん |
| 佐藤 さん |
| 田中 さん |
| 中村 さん |
| 小林 さん |
(18 行 … うち先頭 5 行を表示)
⚠️ この書き方は SQL Server では使えません(|| による文字列連結)。製品ごとの対応表
SELECT CONCAT(name, ' さん') AS keishou
FROM shain
ORDER BY shain_id;| keishou |
|---|
| 鈴木 さん |
| 佐藤 さん |
| 田中 さん |
| 中村 さん |
| 小林 さん |
(18 行 … うち先頭 5 行を表示)
同じ結果です。ここまでは書き方の好みの問題に見えます。
02⚠️ よくある間違い:NULL が混ざると結果が変わる
結論: || は NULL が 1 つでも混ざると全体が NULL になり、CONCAT() は NULL を空文字として扱います。
これは好みの差ではなく挙動の差です。所属が未設定の社員を外部結合で引くと、部署名が NULL になります。そこで || を使うと、名前まで含めて行全体の値が消えます。
エラーは出ません。18 行のうち 2 行だけが空欄になるので、件数の少ないデータでは気づかないまま本番に出ます。
対処は 2 つ。
CONCAT()を使う … NULL の部分だけが空文字になるCOALESCE()で先に埋める … 「所属なし」のような意味のある文字を出せる
表示に使うなら 2 つ目が親切です。空欄より「所属なし」と書いてあるほうが、データが無いのか処理を間違えたのかが読み手に伝わります。
SELECT s.name, b.busho_name,
s.name || '/' || b.busho_name AS pipe_de
FROM shain s
LEFT JOIN busho b ON b.busho_id = s.busho_id
ORDER BY s.shain_id;| name | busho_name | pipe_de |
|---|---|---|
| 鈴木 | 経営 | 鈴木/経営 |
| 佐藤 | 開発 | 佐藤/開発 |
| 田中 | 開発 | 田中/開発 |
| 中村 | 開発 | 中村/開発 |
| 小林 | 開発 | 小林/開発 |
| 加藤 | 基盤 | 加藤/基盤 |
| 吉田 | 基盤 | 吉田/基盤 |
| 山田 | 営業 | 山田/営業 |
| 高橋 | 営業 | 高橋/営業 |
| 伊藤 | 営業 | 伊藤/営業 |
| 松本 | 営業 | 松本/営業 |
| 井上 | マーケ | 井上/マーケ |
| 木村 | マーケ | 木村/マーケ |
| 清水 | 管理 | 清水/管理 |
| 斎藤 | 管理 | 斎藤/管理 |
| 山本 | 管理 | 山本/管理 |
| 渡辺 | NULL | NULL |
| 大野 | NULL | NULL |
(18 行)
末尾の 2 行(渡辺・大野)は名前も消えて NULL です。部署名が NULL なだけなのに、つないだ結果は全部失われます。
⚠️ この書き方は SQL Server では使えません(|| による文字列連結)。製品ごとの対応表
SELECT s.name, b.busho_name,
CONCAT(s.name, '/', b.busho_name) AS concat_de
FROM shain s
LEFT JOIN busho b ON b.busho_id = s.busho_id
ORDER BY s.shain_id;| name | busho_name | concat_de |
|---|---|---|
| 鈴木 | 経営 | 鈴木/経営 |
| 佐藤 | 開発 | 佐藤/開発 |
| 田中 | 開発 | 田中/開発 |
| 中村 | 開発 | 中村/開発 |
| 小林 | 開発 | 小林/開発 |
| 加藤 | 基盤 | 加藤/基盤 |
| 吉田 | 基盤 | 吉田/基盤 |
| 山田 | 営業 | 山田/営業 |
| 高橋 | 営業 | 高橋/営業 |
| 伊藤 | 営業 | 伊藤/営業 |
| 松本 | 営業 | 松本/営業 |
| 井上 | マーケ | 井上/マーケ |
| 木村 | マーケ | 木村/マーケ |
| 清水 | 管理 | 清水/管理 |
| 斎藤 | 管理 | 斎藤/管理 |
| 山本 | 管理 | 山本/管理 |
| 渡辺 | NULL | 渡辺/ |
| 大野 | NULL | 大野/ |
(18 行)
末尾の 2 行が「渡辺/」「大野/」になりました。NULL の部分だけが空文字として扱われています。
SELECT s.name, b.busho_name,
s.name || '/' || COALESCE(b.busho_name, '所属なし') AS umeta
FROM shain s
LEFT JOIN busho b ON b.busho_id = s.busho_id
ORDER BY s.shain_id;| name | busho_name | umeta |
|---|---|---|
| 鈴木 | 経営 | 鈴木/経営 |
| 佐藤 | 開発 | 佐藤/開発 |
| 田中 | 開発 | 田中/開発 |
| 中村 | 開発 | 中村/開発 |
| 小林 | 開発 | 小林/開発 |
| 加藤 | 基盤 | 加藤/基盤 |
| 吉田 | 基盤 | 吉田/基盤 |
| 山田 | 営業 | 山田/営業 |
| 高橋 | 営業 | 高橋/営業 |
| 伊藤 | 営業 | 伊藤/営業 |
| 松本 | 営業 | 松本/営業 |
| 井上 | マーケ | 井上/マーケ |
| 木村 | マーケ | 木村/マーケ |
| 清水 | 管理 | 清水/管理 |
| 斎藤 | 管理 | 斎藤/管理 |
| 山本 | 管理 | 山本/管理 |
| 渡辺 | NULL | 渡辺/所属なし |
| 大野 | NULL | 大野/所属なし |
(18 行)
「渡辺/所属なし」になりました。空欄ではなく意味のある文字が出るので、読み手が判断できます。
⚠️ この書き方は SQL Server では使えません(|| による文字列連結)。製品ごとの対応表
03文字数は LENGTH で数える
結論: LENGTH() が返すのは文字数です。 この実行環境では、全角文字も 1 文字として数えます。
「バイト数ではないか」と迷う部分ですが、製品と型と設定によって答えが変わります。バイト数を返す製品もあれば、文字数を返す製品もあります。日本語を扱うなら、自分の環境でどちらなのかを 1 回試して確かめてください。
⚠️ SQL Server に LENGTH はありません(LEN を使います)。名前が違うだけなので移植は簡単ですが、そのままでは動きません。
SELECT busho_name, LENGTH(busho_name) AS moji
FROM busho
ORDER BY busho_id;| busho_name | moji |
|---|---|
| 経営 | 2 |
| 開発 | 2 |
| 営業 | 2 |
| 管理 | 2 |
| 基盤 | 2 |
| 法務 | 2 |
| マーケ | 3 |
(7 行)
漢字 2 文字の部署は 2、カタカナ 3 文字の「マーケ」は 3 です。バイト数なら 6 や 9 になるので、この環境は文字数を返していると分かります。
⚠️ この書き方は SQL Server では使えません(LENGTH(文字数))。製品ごとの対応表
04大文字小文字は UPPER / LOWER でそろえる
結論: UPPER() と LOWER() は英字の大小を変換します。 日本語には効きません。
いちばんの使い道は比較の前処理です。文字列の比較や LIKE は、環境によって大文字小文字を区別したりしなかったりします。両側を LOWER() で揃えてから比べれば、どの環境でも同じ結果になります。
WHERE LOWER(mail) = LOWER('Taro@Example.com')
⚠️ ただし、列に関数をかけるとその列の索引が使えなくなります。件数の多い表で常用するなら、保存するときに小文字で揃えておくか、式に対する索引を作るのが定石です。
SELECT UPPER('abcXYZ') AS ue,
LOWER('abcXYZ') AS shita;| ue | shita |
|---|---|
| ABCXYZ | abcxyz |
(1 行)
SELECT busho_name, UPPER(busho_name) AS ue
FROM busho
ORDER BY busho_id;| busho_name | ue |
|---|---|
| 経営 | 経営 |
| 開発 | 開発 |
| 営業 | 営業 |
| 管理 | 管理 |
(7 行 … うち先頭 4 行を表示)
元の値と同じです。大文字小文字という区別が無い文字は、そのまま返ります。
SELECT 'ABC' = 'abc' AS sonomama,
LOWER('ABC') = LOWER('abc') AS soroeta;| sonomama | soroeta |
|---|---|
| false | true |
(1 行)
左は false、右は true です。この環境は大文字小文字を区別するので、そろえないと一致しません。
05移植しやすい書き方を選ぶ
結論: 同じことができる書き方が複数あるときは、通る製品が多いほうを選んでおくと後で困りません。
- 連結 …
CONCAT()のほうが広く通ります。||は SQL Server で使えません - 文字数 …
LENGTHは SQL Server ではLENです。関数名を変えるだけなので移植の負担は小さめ - 大文字小文字 …
UPPER/LOWERはどの製品にもあります
各例の下に出ている注記は、構文ごとに調べた対応表から自動で導いたものです。手で書いた覚え書きではないので、対応表を更新すれば注記も一緒に変わります。
このページで扱っていない関数(部分文字列の取り出し、前後の空白除去、置換など)は、まだ対応表に載せていないため注記を出せません。練習問題のほうで扱っています。
SELECT CONCAT(name, '(', busho_id, ')') AS hyouji
FROM shain
ORDER BY shain_id;| hyouji |
|---|
| 鈴木(1) |
| 佐藤(2) |
| 田中(2) |
| 中村(2) |
| 小林(2) |
| 加藤(5) |
| 吉田(5) |
| 山田(3) |
| 高橋(3) |
| 伊藤(3) |
| 松本(3) |
| 井上(7) |
| 木村(7) |
| 清水(4) |
| 斎藤(4) |
| 山本(4) |
| 渡辺() |
| 大野() |
(18 行)
busho_id は数値ですが、文字列に直さずに渡せています。末尾 2 行は所属が NULL なので、その部分だけが空になります。
自分で打ってみる
このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。
押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。
解いてみる
読んだだけでは書けるようになりません。実行結果で採点します。
ここで出てくる関数を引く
- CONCAT / ||文字列をつなぐ。|| は NULL が混じると全体が NULL、CONCAT は NULL を飛ばす。
- LENGTH / CHAR_LENGTH文字列の長さを返す。数えるのは文字数で、バイト数ではない。
- LPAD / RPAD指定した長さになるまで、左(LPAD)や右(RPAD)を埋める。長すぎると切り捨てられる。
- POSITION / STRPOS文字列の中で、指定した文字列が最初に現れる位置を返す。見つからないと 0。
- REGEXP_REPLACE正規表現で置き換える。既定では最初の 1 つしか置き換えない。
- REPLACE文字列の中の指定した文字列を、すべて別の文字列に置き換える。
- SPLIT_PART区切り文字で分けて、指定した位置の断片を取り出す。範囲外は空文字。
- SUBSTRING文字列の一部を切り出す。開始位置は 1 から数える。
- TRIM文字列の前後の空白(や指定した文字)を取り除く。
- UPPER / LOWER英字を大文字・小文字にそろえる。比較で大文字小文字を無視したいときに使う。
関連するトピック
- NULL を扱う関数COALESCE で NULL を埋めてから連結する話の続きです。
- LIKE 述語文字列を形で探すときはこちらです。
- 比較演算子文字列の大小がどう決まるかの話です。
- CASE 式値によって出す文字を変えたいときに組み合わせます。
根拠(一次情報)
- PostgreSQL 18 マニュアル: 文字列関数と演算子一次情報・確認 2026-08-03
- MySQL 8.4 リファレンスマニュアル: 文字列関数一次情報・確認 2026-08-03
- Microsoft SQL Server: LEN(Transact-SQL)一次情報・確認 2026-08-03