タイトル: 関数
SEOタイトル: SQL の組み込み関数まとめ(集計 / 文字列 / 日時 / 数値 / 条件 / ユーザー定義)
| この記事の要点 |
|
集計関数 (Aggregate Functions)
複数行の値を 1 つの値に集約する関数。GROUP BY と組み合わせて使うことが多い。
-- 件数
SELECT COUNT(*) FROM orders; -- NULL も含めた全行
SELECT COUNT(email) FROM users; -- NULL を除いた件数
SELECT COUNT(DISTINCT category) FROM products;
-- 合計・平均・最大・最小
SELECT SUM(amount), AVG(amount), MIN(amount), MAX(amount)
FROM orders;
-- グループ別集計
SELECT category, COUNT(*) AS cnt, SUM(price) AS total
FROM products
GROUP BY category
HAVING COUNT(*) > 5
ORDER BY total DESC;
-- 文字列を連結する集計(MySQL: GROUP_CONCAT / PostgreSQL: STRING_AGG)
SELECT category, GROUP_CONCAT(name ORDER BY price SEPARATOR ', ')
FROM products GROUP BY category;
| 関数 | 動作 | NULL の扱い |
|---|---|---|
COUNT(*) | 全行数 | NULL も数える |
COUNT(col) | 非 NULL 件数 | NULL 除外 |
SUM(col) | 合計 | NULL 除外 |
AVG(col) | 平均 | NULL 除外(分母も) |
MIN / MAX(col) | 最小 / 最大 | NULL 除外 |
文字列関数
-- 連結
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;
-- PostgreSQL は || も使える: first_name || ' ' || last_name
-- 部分文字列(1 始まり、長さ指定)
SELECT SUBSTRING(email, 1, 3) FROM users;
-- MySQL: SUBSTRING / SUBSTR / MID
-- PostgreSQL: SUBSTRING(email FROM 1 FOR 3)
-- 大文字・小文字変換
SELECT UPPER(name), LOWER(email) FROM users;
-- 長さ
SELECT LENGTH(name) FROM users; -- バイト数(MySQL)
SELECT CHAR_LENGTH(name) FROM users; -- 文字数
-- 前後の空白除去
SELECT TRIM(' Hello '); -- 'Hello'
SELECT LTRIM(' Hello'), RTRIM('Hello ');
-- 置換
SELECT REPLACE(email, '@old.com', '@new.com') FROM users;
-- パディング
SELECT LPAD('5', 3, '0'); -- '005'
SELECT RPAD('A', 5, '-'); -- 'A----'
-- 位置検索
SELECT INSTR(email, '@') FROM users; -- '@' の位置(1 始まり)
SELECT LOCATE('@', email) FROM users; -- MySQL
-- 文字列を逆順
SELECT REVERSE('Hello'); -- 'olleH'
日時関数
-- 現在日時
SELECT NOW(); -- 2026-06-11 12:34:56
SELECT CURDATE(), CURRENT_DATE; -- 2026-06-11
SELECT CURTIME(), CURRENT_TIME; -- 12:34:56
-- 加算・減算
SELECT DATE_ADD(NOW(), INTERVAL 7 DAY);
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH);
SELECT NOW() + INTERVAL 1 HOUR; -- MySQL の省略形
-- 差分
SELECT DATEDIFF('2026-12-31', '2026-06-11'); -- 日数
SELECT TIMESTAMPDIFF(MONTH, birth, NOW()) -- 月単位 (MySQL)
FROM users;
-- フォーマット
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i'); -- '2026/06/11 12:34'
SELECT DATE_FORMAT(NOW(), '%W'); -- 'Thursday'
-- PostgreSQL は TO_CHAR
SELECT TO_CHAR(NOW(), 'YYYY/MM/DD HH24:MI');
-- 一部要素抽出
SELECT YEAR(NOW()), MONTH(NOW()), DAY(NOW()),
HOUR(NOW()), MINUTE(NOW()), SECOND(NOW());
SELECT EXTRACT(YEAR FROM NOW()); -- 標準 SQL
数値関数
SELECT ROUND(3.567, 2); -- 3.57(小数 2 桁)
SELECT CEIL(3.2); -- 4(切り上げ)= CEILING
SELECT FLOOR(3.8); -- 3(切り捨て)
SELECT TRUNCATE(3.567, 1); -- 3.5(切り捨て、桁指定)MySQL
SELECT ABS(-7); -- 7
SELECT MOD(10, 3); -- 1(余り)= 10 % 3
SELECT POWER(2, 10); -- 1024 = POW
SELECT SQRT(16); -- 4
SELECT RAND(); -- 0.0 〜 1.0 の乱数(MySQL)= RANDOM (PG)
SELECT FLOOR(RAND() * 100); -- 0〜99 の整数乱数
SELECT SIGN(-5), SIGN(0), SIGN(7); -- -1, 0, 1
条件関数 / NULL 関数
-- CASE WHEN: 標準 SQL
SELECT name,
CASE
WHEN score >= 80 THEN 'A'
WHEN score >= 60 THEN 'B'
WHEN score >= 40 THEN 'C'
ELSE 'D'
END AS grade
FROM students;
-- COALESCE: 最初の非 NULL を返す
SELECT COALESCE(nickname, name, 'Anonymous') FROM users;
-- NULLIF: 2 値が同じなら NULL を返す
SELECT NULLIF(deleted_at, '0000-00-00');
-- MySQL: IFNULL(COALESCE と同等の 2 引数版)
SELECT IFNULL(phone, '不明') FROM users;
-- MySQL: IF
SELECT IF(score > 60, '合格', '不合格') FROM students;
-- SQL Server: ISNULL(MySQL の IFNULL 相当、ただし PG の ISNULL とは別物)
SELECT ISNULL(phone, '不明') FROM users; -- SQL Server
ユーザー定義関数 (UDF)
独自の関数を作って繰り返し使う処理をカプセル化できます。
-- MySQL: 税込価格計算関数
DELIMITER //
CREATE FUNCTION calc_tax_price(price INT, rate DECIMAL(3,2))
RETURNS INT
DETERMINISTIC
BEGIN
RETURN FLOOR(price * (1 + rate));
END //
DELIMITER ;
-- 使用
SELECT name, calc_tax_price(price, 0.10) AS tax_inc
FROM products;
-- PostgreSQL: PL/pgSQL
CREATE OR REPLACE FUNCTION calc_tax_price(price INT, rate NUMERIC)
RETURNS INT AS $$
BEGIN
RETURN FLOOR(price * (1 + rate));
END;
$$ LANGUAGE plpgsql;
ウィンドウ関数 (OVER 句)
SQL:2003 で導入された強力な関数群。集計関数を「グループに集約せず」行ごとに付与できます。
-- カテゴリ別ランキング
SELECT name, category, price,
RANK() OVER (PARTITION BY category ORDER BY price DESC) AS rk,
DENSE_RANK() OVER (PARTITION BY category ORDER BY price DESC) AS dr,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS rn
FROM products;
-- 累積合計
SELECT date, amount,
SUM(amount) OVER (ORDER BY date) AS running_total
FROM sales;
-- 直前・直後の値
SELECT date, amount,
LAG(amount, 1) OVER (ORDER BY date) AS prev_amount,
LEAD(amount, 1) OVER (ORDER BY date) AS next_amount
FROM sales;
FAQ
Q: COUNT(*) と COUNT(1) はどちらが速い?
A: 現代の DB(MySQL 8 / PG 12+)では同等。最適化されているので可読性の高い COUNT(*) を推奨。
Q: 文字列連結が CONCAT でうまく動かない
A: NULL が混ざると結果が NULL になる DB が多い(標準 SQL の || も同様)。MySQL の CONCAT_WS(sep, ...) を使うか COALESCE で NULL を空文字に変換してください。
Q: 文字列の長さは LENGTH か CHAR_LENGTH か
A: 日本語など multi-byte 文字を含む場合は CHAR_LENGTH(文字数)。LENGTH はバイト数で UTF-8 では日本語 1 文字が 3 バイトになります。