30分钟搞定MySQL常用函数之字符串、日期、聚合函数(附实操代码)

来源:这里教程网 时间:2026-04-02 16:16:03 作者:
一、字符串函数1. 字符串长度函数LENGTH()和CHAR_LENGTH()示例:Java 代码示例:代码解释2. 字符串截取函数SUBSTRING()/SUBSTR()和LEFT()/RIGHT()示例:Java 代码示例:代码解释3. 字符串替换函数REPLACE()示例:Java 代码示例:代码解释4. 大小写转换函数UPPER()/UCASE()和LOWER()/LCASE()示例:Java 代码示例:代码解释5. 去除空格函数TRIM()/LTRIM()/RTRIM()示例:Java 代码示例:代码解释6. 字符串连接函数CONCAT()示例:Java 代码示例:代码解释二、日期函数1. 当前日期和时间函数NOW(),CURDATE(),CURTIME()示例:Java 代码示例:代码解释2. 日期和时间组件提取函数YEAR(),MONTH(),DAY(),HOUR(),MINUTE(),SECOND()示例:Java 代码示例:代码解释3. 日期计算函数DATE_ADD()/ADDDATE(),DATE_SUB()/SUBDATE()示例:Java 代码示例:代码解释4. 日期差函数DATEDIFF()和TIMESTAMPDIFF()示例:Java 代码示例:代码解释5. 日期格式化函数DATE_FORMAT()示例:Java 代码示例:代码解释三、聚合函数1. 计数函数COUNT()示例:Java 代码示例:代码解释2. 求和函数SUM()示例:Java 代码示例:代码解释3. 平均值函数AVG()示例:Java 代码示例:代码解释4. 最大值和最小值函数MAX()和MIN()示例:Java 代码示例:代码解释5. 聚合函数与分组GROUP BY结合使用示例:Java 代码示例:代码解释四、综合实战:销售数据分析实战目标数据准备Java 代码示例代码解释总结:函数是数据库操作的利器Mermaid 图表:字符串函数关系图 Mermaid 图表:日期函数关系图 Mermaid 图表:聚合函数关系图 Mermaid 图表:函数综合应用示意图 总结

30分钟搞定MySQL常用函数之字符串、日期、聚合函数(附实操代码)

在数据库的世界里,函数就像是程序员的工具箱,为我们提供了强大的数据处理能力。无论是清洗数据、格式化输出、计算时间差,还是进行统计分析,MySQL 函数都能大显身手。今天,我们将一起深入探索 MySQL 中最常用的几类函数:字符串函数日期函数聚合函数。我们将通过丰富的示例和 Java 代码来演示如何在实际项目中运用这些函数,让你在 30 分钟内轻松掌握它们!

一、字符串函数

字符串函数是处理文本数据的基础。在日常开发中,我们经常需要对字符串进行截取、替换、大小写转换、填充等操作。MySQL 提供了大量内置的字符串函数来简化这些任务。

1. 字符串长度函数LENGTH()和CHAR_LENGTH()

LENGTH()CHAR_LENGTH() 都用来计算字符串的长度,但它们的计算方式不同:

LENGTH(str):计算字符串的字节长度。对于单字节字符集(如 Latin1),它等于字符数;对于多字节字符集(如 UTF8),一个字符可能占用多个字节。CHAR_LENGTH(str):计算字符串的字符数,忽略字符编码的差异。

示例:

-- 假设表 users 存储了用户名和描述信息 -- INSERT INTO users (name, description) VALUES ('Alice', 'Hello World'), ('张三', '你好世界'); SELECT name, description, LENGTH(description) AS byte_length, -- 计算字节长度 CHAR_LENGTH(description) AS char_length -- 计算字符长度 FROM users; -- 结果可能类似于: -- name | description | byte_length | char_length -- Alice | Hello World | 11 | 11 -- 张三 | 你好世界 | 12 | 4 (UTF8下中文字符占3字节)

Java 代码示例:

import java.sql.*; import java.util.ArrayList; import java.util.List; public class StringFunctionExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateLengthFunctions() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS sample_users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), description TEXT ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO sample_users (name, description) VALUES (?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "Alice"); pstmt.setString(2, "Hello World"); pstmt.addBatch(); pstmt.setString(1, "张三"); pstmt.setString(2, "你好世界"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 测试数据已插入。"); } // 查询并展示 LENGTH 和 CHAR_LENGTH String querySQL = """ SELECT name, description, LENGTH(description) AS byte_length, CHAR_LENGTH(description) AS char_length FROM sample_users """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 字符串长度函数演示 ==="); System.out.printf("%-10s %-15s %-15s %-15s%n", "姓名", "描述", "字节长度", "字符长度"); while (rs.next()) { String name = rs.getString("name"); String description = rs.getString("description"); int byteLen = rs.getInt("byte_length"); int charLen = rs.getInt("char_length"); System.out.printf("%-10s %-15s %-15d %-15d%n", name, description, byteLen, charLen); } } } catch (SQLException e) { System.err.println("执行字符串长度函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateLengthFunctions(); } }

代码解释

    创建表:首先创建一个 sample_users 表用于存储示例数据。插入数据:使用 PreparedStatement 批量插入包含英文和中文的测试数据。查询与展示:执行 SQL 查询,同时使用 LENGTH()CHAR_LENGTH() 计算 description 字段的长度,并在 Java 控制台打印结果。输出对比:对于中文字符,LENGTH() 返回的是字节数(每个中文字符通常占 3 个字节),而 CHAR_LENGTH() 返回的是字符数。

2. 字符串截取函数SUBSTRING()/SUBSTR()和LEFT()/RIGHT()

这些函数用于从字符串中提取一部分。

SUBSTRING(str, pos, len):从 str 的位置 pos 开始,截取长度为 len 的子字符串。pos 从 1 开始计数。LEFT(str, len):从字符串左边开始截取 len 个字符。RIGHT(str, len):从字符串右边开始截取 len 个字符。

示例:

-- 假设有一个产品名称字段 product_name -- SELECT SUBSTRING('MySQL Database', 1, 5) AS result; -- 输出: MySQL -- SELECT LEFT('MySQL Database', 5) AS result; -- 输出: MySQL -- SELECT RIGHT('MySQL Database', 7) AS result; -- 输出: Database -- SELECT SUBSTRING('MySQL Database', 7) AS result; -- 输出: Database (从位置7开始到末尾) -- SELECT SUBSTRING('MySQL Database', 7, 4) AS result; -- 输出: Dat (从位置7开始,截取4个字符)

Java 代码示例:

import java.sql.*; public class SubstringExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateSubstringFunctions() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS products ( id INT AUTO_INCREMENT PRIMARY KEY, product_name VARCHAR(255), product_code VARCHAR(50) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO products (product_name, product_code) VALUES (?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "iPhone 14 Pro Max"); pstmt.setString(2, "IP14PM001"); pstmt.addBatch(); pstmt.setString(1, "Samsung Galaxy S23 Ultra"); pstmt.setString(2, "SGS23U002"); pstmt.addBatch(); pstmt.setString(1, "MacBook Air M2"); pstmt.setString(2, "MA2M003"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 产品数据已插入。"); } // 演示截取函数 String querySQL = """ SELECT product_name, product_code, LEFT(product_name, 5) AS brand_name, -- 截取前5个字符作为品牌名 RIGHT(product_code, 3) AS last_three_digits, -- 截取产品代码后3位 SUBSTRING(product_name, 1, 5) AS substring_brand, -- 同样是前5个字符 SUBSTRING(product_code, 4, 3) AS middle_part_code -- 从位置4开始截取3个字符 FROM products """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 字符串截取函数演示 ==="); System.out.printf("%-25s %-15s %-15s %-15s %-15s %-15s%n", "产品名称", "产品代码", "品牌名", "后三位", "截取品牌", "中间代码"); while (rs.next()) { String productName = rs.getString("product_name"); String productCode = rs.getString("product_code"); String brandName = rs.getString("brand_name"); String lastThreeDigits = rs.getString("last_three_digits"); String substringBrand = rs.getString("substring_brand"); String middlePartCode = rs.getString("middle_part_code"); System.out.printf("%-25s %-15s %-15s %-15s %-15s %-15s%n", productName, productCode, brandName, lastThreeDigits, substringBrand, middlePartCode); } } } catch (SQLException e) { System.err.println("执行字符串截取函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateSubstringFunctions(); } }

代码解释

    创建表:创建 products 表存储产品信息。插入数据:插入三条包含产品名称和代码的测试数据。查询与演示使用 LEFT(product_name, 5) 提取产品名称的前 5 个字符作为品牌名。使用 RIGHT(product_code, 3) 提取产品代码的后 3 个字符。使用 SUBSTRING(product_name, 1, 5)LEFT 类似,但更灵活。使用 SUBSTRING(product_code, 4, 3) 从产品代码的第 4 个字符开始截取 3 个字符。输出结果:清晰地展示了不同截取函数的效果。

3. 字符串替换函数REPLACE()

REPLACE(str, from_str, to_str) 用于将字符串 str 中所有出现的 from_str 替换为 to_str

示例:

-- SELECT REPLACE('Hello World', 'World', 'MySQL') AS result; -- 输出: Hello MySQL -- SELECT REPLACE('abcabcabc', 'bc', 'XY') AS result; -- 输出: aXYaXYaXY

Java 代码示例:

import java.sql.*; public class ReplaceExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateReplaceFunction() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS user_profiles ( id INT AUTO_INCREMENT PRIMARY KEY, full_name VARCHAR(100), email VARCHAR(100), bio TEXT ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO user_profiles (full_name, email, bio) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "张小明"); pstmt.setString(2, "zhang.xiaoming@example.com"); pstmt.setString(3, "我是张小明,热爱技术。联系方式:zhang.xiaoming@example.com"); pstmt.addBatch(); pstmt.setString(1, "李丽"); pstmt.setString(2, "li.li@company.com"); pstmt.setString(3, "李丽,软件工程师。邮箱地址:li.li@company.com"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 用户资料已插入。"); } // 演示替换函数 String querySQL = """ SELECT full_name, email, bio, REPLACE(bio, '联系方式', '联系信息') AS modified_bio, -- 替换“联系方式”为“联系信息” REPLACE(email, '@', '[AT]') AS masked_email -- 用 [AT] 替换 @ 符号(仅演示) FROM user_profiles """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 字符串替换函数演示 ==="); System.out.printf("%-10s %-25s %-50s %-50s %-25s%n", "姓名", "邮箱", "原始简介", "修改后简介", "屏蔽邮箱"); while (rs.next()) { String fullName = rs.getString("full_name"); String email = rs.getString("email"); String bio = rs.getString("bio"); String modifiedBio = rs.getString("modified_bio"); String maskedEmail = rs.getString("masked_email"); System.out.printf("%-10s %-25s %-50s %-50s %-25s%n", fullName, email, bio, modifiedBio, maskedEmail); } } } catch (SQLException e) { System.err.println("执行字符串替换函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateReplaceFunction(); } }

代码解释

    创建表:创建 user_profiles 表存储用户信息。插入数据:插入包含用户姓名、邮箱和简介的测试数据,其中简介中包含了邮箱地址。查询与演示使用 REPLACE(bio, '联系方式', '联系信息') 将简介中的“联系方式”替换为“联系信息”。使用 REPLACE(email, '@', '[AT]') 将邮箱地址中的 @ 符号替换为 [AT],用于演示(实际应用中可能用于脱敏)。输出结果:展示原始数据和替换后的效果。

4. 大小写转换函数UPPER()/UCASE()和LOWER()/LCASE()

这些函数用于将字符串转换为大写或小写。

UPPER(str) / UCASE(str):将字符串转换为大写。LOWER(str) / LCASE(str):将字符串转换为小写。

示例:

-- SELECT UPPER('hello world') AS upper_result; -- 输出: HELLO WORLD -- SELECT LOWER('HELLO WORLD') AS lower_result; -- 输出: hello world -- SELECT UCASE('mysql') AS ucase_result; -- 输出: MYSQL -- SELECT LCASE('MYSQL') AS lcase_result; -- 输出: mysql

Java 代码示例:

import java.sql.*; public class CaseConversionExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateCaseConversion() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS employees ( id INT AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(50), last_name VARCHAR(50), department VARCHAR(50) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO employees (first_name, last_name, department) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "john"); pstmt.setString(2, "doe"); pstmt.setString(3, "IT"); pstmt.addBatch(); pstmt.setString(1, "Jane"); pstmt.setString(2, "SMITH"); pstmt.setString(3, "HR"); pstmt.addBatch(); pstmt.setString(1, "michael"); pstmt.setString(2, "Brown"); pstmt.setString(3, "Finance"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 员工数据已插入。"); } // 演示大小写转换 String querySQL = """ SELECT first_name, last_name, department, UPPER(first_name) AS upper_first_name, LOWER(last_name) AS lower_last_name, CONCAT(UPPER(SUBSTRING(first_name, 1, 1)), LOWER(SUBSTRING(first_name, 2))) AS title_case_first_name -- 简单标题格式 FROM employees """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 大小写转换函数演示 ==="); System.out.printf("%-10s %-10s %-10s %-20s %-20s %-30s%n", "名", "姓", "部门", "大写名", "小写姓", "标题格式名"); while (rs.next()) { String firstName = rs.getString("first_name"); String lastName = rs.getString("last_name"); String department = rs.getString("department"); String upperFirstName = rs.getString("upper_first_name"); String lowerLastName = rs.getString("lower_last_name"); String titleCaseFirstName = rs.getString("title_case_first_name"); System.out.printf("%-10s %-10s %-10s %-20s %-20s %-30s%n", firstName, lastName, department, upperFirstName, lowerLastName, titleCaseFirstName); } } } catch (SQLException e) { System.err.println("执行大小写转换函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateCaseConversion(); } }

代码解释

    创建表:创建 employees 表存储员工信息。插入数据:插入包含员工名、姓和部门的测试数据,其中名字和姓氏大小写不统一。查询与演示使用 UPPER(first_name) 将名字转换为大写。使用 LOWER(last_name) 将姓氏转换为小写。使用 CONCAT(UPPER(SUBSTRING(first_name, 1, 1)), LOWER(SUBSTRING(first_name, 2))) 实现简单的标题格式(首字母大写,其余小写)。输出结果:展示了原始数据和各种大小写转换后的效果。

5. 去除空格函数TRIM()/LTRIM()/RTRIM()

这些函数用于去除字符串两端或特定一侧的空白字符。

TRIM(str):去除字符串两端的空白字符。LTRIM(str):去除字符串左侧的空白字符。RTRIM(str):去除字符串右侧的空白字符。TRIM([leading|trailing|both] [remstr FROM] str):更详细的语法,可以指定去除哪一侧的空白或指定要移除的字符。

示例:

-- SELECT TRIM(' Hello ') AS result; -- 输出: Hello -- SELECT LTRIM(' Hello') AS result; -- 输出: Hello -- SELECT RTRIM('Hello ') AS result; -- 输出: Hello -- SELECT TRIM('x' FROM 'xxxHelloxxx') AS result; -- 输出: Hello (去除两边的 'x') -- SELECT TRIM(LEADING 'x' FROM 'xxxHello') AS result; -- 输出: Hello (只去除开头的 'x')

Java 代码示例:

import java.sql.*; public class TrimExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateTrimFunctions() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS user_input_logs ( id INT AUTO_INCREMENT PRIMARY KEY, raw_data VARCHAR(255), -- 原始输入 processed_data VARCHAR(255) -- 处理后的数据 ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据(包含前后空格) String insertSQL = "INSERT INTO user_input_logs (raw_data, processed_data) VALUES (?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, " John Doe "); pstmt.setString(2, ""); pstmt.addBatch(); pstmt.setString(1, "\t\tAlice Smith\n\n"); pstmt.setString(2, ""); pstmt.addBatch(); pstmt.setString(1, " \r\nBob Johnson "); pstmt.setString(2, ""); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 用户输入日志已插入。"); } // 演示去空格函数 String querySQL = """ SELECT raw_data, TRIM(raw_data) AS trimmed_data, LTRIM(raw_data) AS left_trimmed_data, RTRIM(raw_data) AS right_trimmed_data, TRIM('\r\n\t ' FROM raw_data) AS clean_data -- 移除多种空白字符 FROM user_input_logs """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 去除空格函数演示 ==="); System.out.printf("%-25s %-25s %-25s %-25s %-25s%n", "原始数据", "两端去空格", "左去空格", "右去空格", "清理后数据"); while (rs.next()) { String rawData = rs.getString("raw_data"); String trimmedData = rs.getString("trimmed_data"); String leftTrimmedData = rs.getString("left_trimmed_data"); String rightTrimmedData = rs.getString("right_trimmed_data"); String cleanData = rs.getString("clean_data"); System.out.printf("%-25s %-25s %-25s %-25s %-25s%n", rawData, trimmedData, leftTrimmedData, rightTrimmedData, cleanData); } } } catch (SQLException e) { System.err.println("执行去空格函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateTrimFunctions(); } }

代码解释

    创建表:创建 user_input_logs 表用于记录用户输入。插入数据:插入包含前后空格、制表符、换行符的原始数据。查询与演示使用 TRIM(raw_data) 去除两端的空白字符。使用 LTRIM(raw_data) 去除左侧空白。使用 RTRIM(raw_data) 去除右侧空白。使用 TRIM('\r\n\t ' FROM raw_data) 去除指定的多种空白字符。输出结果:展示了原始数据和各种去空格处理后的效果。

6. 字符串连接函数CONCAT()

CONCAT(str1, str2, ...) 用于将多个字符串连接成一个字符串。如果任意一个参数为 NULL,则结果为 NULL

示例:

-- SELECT CONCAT('Hello', ' ', 'World') AS result; -- 输出: Hello World -- SELECT CONCAT('User:', 'John') AS result; -- 输出: User:John -- SELECT CONCAT('Name: ', first_name, ' ', last_name) AS full_name FROM users; -- 连接姓名字段 -- SELECT CONCAT_WS('-', '2023', '10', '15') AS date_string; -- 使用分隔符连接,CONCAT_WS

Java 代码示例:

import java.sql.*; public class ConcatExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateConcatFunction() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS addresses ( id INT AUTO_INCREMENT PRIMARY KEY, street VARCHAR(255), city VARCHAR(100), state VARCHAR(100), zip_code VARCHAR(20) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO addresses (street, city, state, zip_code) VALUES (?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "123 Main Street"); pstmt.setString(2, "New York"); pstmt.setString(3, "NY"); pstmt.setString(4, "10001"); pstmt.addBatch(); pstmt.setString(1, "456 Oak Avenue"); pstmt.setString(2, "Los Angeles"); pstmt.setString(3, "CA"); pstmt.setString(4, "90210"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 地址数据已插入。"); } // 演示字符串连接 String querySQL = """ SELECT street, city, state, zip_code, CONCAT(street, ', ', city, ', ', state, ' ', zip_code) AS full_address, CONCAT_WS(', ', street, city, state, zip_code) AS formatted_address -- 使用分隔符 FROM addresses """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 字符串连接函数演示 ==="); System.out.printf("%-25s %-20s %-10s %-10s %-50s %-50s%n", "街道", "城市", "州", "邮编", "完整地址", "格式化地址"); while (rs.next()) { String street = rs.getString("street"); String city = rs.getString("city"); String state = rs.getString("state"); String zipCode = rs.getString("zip_code"); String fullAddress = rs.getString("full_address"); String formattedAddress = rs.getString("formatted_address"); System.out.printf("%-25s %-20s %-10s %-10s %-50s %-50s%n", street, city, state, zipCode, fullAddress, formattedAddress); } } } catch (SQLException e) { System.err.println("执行字符串连接函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateConcatFunction(); } }

代码解释

    创建表:创建 addresses 表存储地址信息。插入数据:插入包含街道、城市、州、邮编的测试数据。查询与演示使用 CONCAT(street, ', ', city, ', ', state, ' ', zip_code) 将地址信息拼接成一个完整的地址字符串。使用 CONCAT_WS(', ', street, city, state, zip_code) 使用逗号作为分隔符拼接地址,CONCAT_WSCONCAT 的变体,专门用于带分隔符的连接。输出结果:展示了原始数据和拼接后的地址效果。

二、日期函数

日期函数是处理时间相关数据的强大工具。在应用开发中,我们经常需要计算日期差、提取日期组件、格式化日期显示、处理时间戳等。MySQL 提供了丰富的日期函数来应对这些需求。

1. 当前日期和时间函数NOW(),CURDATE(),CURTIME()

这些函数用于获取当前的日期、时间和时间戳。

NOW():返回当前日期和时间(YYYY-MM-DD HH:MM:SS 格式)。CURDATE():返回当前日期(YYYY-MM-DD 格式)。CURTIME():返回当前时间(HH:MM:SS 格式)。

示例:

-- SELECT NOW() AS current_datetime; -- 输出: 2023-10-15 14:30:45 -- SELECT CURDATE() AS current_date; -- 输出: 2023-10-15 -- SELECT CURTIME() AS current_time; -- 输出: 14:30:45

Java 代码示例:

import java.sql.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class DateTimeFunctionExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateDateTimeFunctions() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS events ( id INT AUTO_INCREMENT PRIMARY KEY, event_name VARCHAR(255), event_date DATE, created_at DATETIME, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO events (event_name, event_date, created_at) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "产品发布会"); pstmt.setDate(2, Date.valueOf("2023-11-20")); // 使用 java.sql.Date pstmt.setTimestamp(3, Timestamp.valueOf("2023-10-15 10:00:00")); // 使用 java.sql.Timestamp pstmt.addBatch(); pstmt.setString(1, "团队建设活动"); pstmt.setDate(2, Date.valueOf("2023-12-05")); pstmt.setTimestamp(3, Timestamp.valueOf("2023-10-15 11:30:00")); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 事件数据已插入。"); } // 演示日期时间函数 String querySQL = """ SELECT event_name, event_date, created_at, NOW() AS db_now, -- 数据库当前时间 CURDATE() AS db_current_date, -- 数据库当前日期 CURTIME() AS db_current_time, -- 数据库当前时间 DATE(created_at) AS created_date_only, -- 从DATETIME中提取日期 TIME(created_at) AS created_time_only, -- 从DATETIME中提取时间 DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') AS formatted_created_at -- 格式化时间 FROM events """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 日期时间函数演示 ==="); System.out.printf("%-20s %-12s %-20s %-20s %-12s %-12s %-12s %-12s %-20s%n", "事件名称", "事件日期", "创建时间", "数据库当前时间", "当前日期", "当前时间", "日期", "时间", "格式化时间"); while (rs.next()) { String eventName = rs.getString("event_name"); Date eventDate = rs.getDate("event_date"); Timestamp createdAt = rs.getTimestamp("created_at"); Timestamp dbNow = rs.getTimestamp("db_now"); Date dbCurrentDate = rs.getDate("db_current_date"); Time dbCurrentTime = rs.getTime("db_current_time"); Date createdDateOnly = rs.getDate("created_date_only"); Time createdTimeOnly = rs.getTime("created_time_only"); String formattedCreatedAt = rs.getString("formatted_created_at"); System.out.printf("%-20s %-12s %-20s %-20s %-12s %-12s %-12s %-12s %-20s%n", eventName, eventDate, createdAt, dbNow, dbCurrentDate, dbCurrentTime, createdDateOnly, createdTimeOnly, formattedCreatedAt); } } } catch (SQLException e) { System.err.println("执行日期时间函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateDateTimeFunctions(); } }

代码解释

    创建表:创建 events 表存储事件信息,包含日期和时间字段。插入数据:插入两条事件记录,包含事件名称、日期和创建时间。查询与演示使用 NOW() 获取数据库服务器的当前时间。使用 CURDATE()CURTIME() 获取当前日期和时间。使用 DATE(created_at)DATETIME 字段中提取日期部分。使用 TIME(created_at)DATETIME 字段中提取时间部分。使用 DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') 格式化时间显示。输出结果:展示了原始数据和各种日期时间函数的处理结果。

2. 日期和时间组件提取函数YEAR(),MONTH(),DAY(),HOUR(),MINUTE(),SECOND()

这些函数用于从日期时间值中提取特定的组件。

示例:

-- SELECT YEAR('2023-10-15') AS year_value; -- 输出: 2023 -- SELECT MONTH('2023-10-15') AS month_value; -- 输出: 10 -- SELECT DAY('2023-10-15') AS day_value; -- 输出: 15 -- SELECT HOUR('2023-10-15 14:30:45') AS hour_value; -- 输出: 14 -- SELECT MINUTE('2023-10-15 14:30:45') AS minute_value; -- 输出: 30 -- SELECT SECOND('2023-10-15 14:30:45') AS second_value; -- 输出: 45

Java 代码示例:

import java.sql.*; public class ExtractDateTimeComponentsExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateExtractDateTimeComponents() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS orders ( id INT AUTO_INCREMENT PRIMARY KEY, customer_name VARCHAR(100), order_date DATETIME, delivery_status ENUM('Pending', 'Shipped', 'Delivered') ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO orders (customer_name, order_date, delivery_status) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "张三"); pstmt.setTimestamp(2, Timestamp.valueOf("2023-10-10 14:20:00")); pstmt.setString(3, "Shipped"); pstmt.addBatch(); pstmt.setString(1, "李四"); pstmt.setTimestamp(2, Timestamp.valueOf("2023-10-12 09:15:30")); pstmt.setString(3, "Pending"); pstmt.addBatch(); pstmt.setString(1, "王五"); pstmt.setTimestamp(2, Timestamp.valueOf("2023-10-14 16:45:10")); pstmt.setString(3, "Delivered"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 订单数据已插入。"); } // 演示提取日期时间组件 String querySQL = """ SELECT customer_name, order_date, YEAR(order_date) AS order_year, MONTH(order_date) AS order_month, DAY(order_date) AS order_day, HOUR(order_date) AS order_hour, MINUTE(order_date) AS order_minute, SECOND(order_date) AS order_second, CONCAT(YEAR(order_date), '-', LPAD(MONTH(order_date), 2, '0')) AS year_month -- 组合成 YYYY-MM 格式 FROM orders """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 日期时间组件提取演示 ==="); System.out.printf("%-10s %-20s %-10s %-10s %-10s %-10s %-10s %-10s %-15s%n", "客户名", "下单时间", "年", "月", "日", "小时", "分钟", "秒", "年月"); while (rs.next()) { String customerName = rs.getString("customer_name"); Timestamp orderDate = rs.getTimestamp("order_date"); int orderYear = rs.getInt("order_year"); int orderMonth = rs.getInt("order_month"); int orderDay = rs.getInt("order_day"); int orderHour = rs.getInt("order_hour"); int orderMinute = rs.getInt("order_minute"); int orderSecond = rs.getInt("order_second"); String yearMonth = rs.getString("year_month"); System.out.printf("%-10s %-20s %-10d %-10d %-10d %-10d %-10d %-10d %-15s%n", customerName, orderDate, orderYear, orderMonth, orderDay, orderHour, orderMinute, orderSecond, yearMonth); } } } catch (SQLException e) { System.err.println("执行日期时间组件提取演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateExtractDateTimeComponents(); } }

代码解释

    创建表:创建 orders 表存储订单信息。插入数据:插入三条包含客户名和下单时间的订单记录。查询与演示使用 YEAR(order_date), MONTH(order_date), DAY(order_date) 等函数提取年、月、日。使用 HOUR(order_date), MINUTE(order_date), SECOND(order_date) 提取时、分、秒。使用 CONCAT(YEAR(order_date), '-', LPAD(MONTH(order_date), 2, '0')) 将年份和月份组合成 YYYY-MM 格式(使用 LPAD 确保月份是两位数)。输出结果:展示了原始时间数据和各个组件的提取结果。

3. 日期计算函数DATE_ADD()/ADDDATE(),DATE_SUB()/SUBDATE()

这些函数用于对日期进行加减运算。

DATE_ADD(date, INTERVAL value unit)ADDDATE(date, INTERVAL value unit):给日期加上一个时间间隔。DATE_SUB(date, INTERVAL value unit)SUBDATE(date, INTERVAL value unit):从日期中减去一个时间间隔。INTERVAL 后面跟着值和单位(如 DAY, MONTH, YEAR, HOUR, MINUTE, SECOND)。

示例:

-- SELECT DATE_ADD('2023-10-15', INTERVAL 1 DAY) AS next_day; -- 输出: 2023-10-16 -- SELECT DATE_SUB('2023-10-15', INTERVAL 1 MONTH) AS last_month; -- 输出: 2023-09-15 -- SELECT DATE_ADD('2023-10-15 14:30:45', INTERVAL 1 HOUR) AS plus_one_hour; -- 输出: 2023-10-15 15:30:45 -- SELECT ADDDATE('2023-10-15', INTERVAL 7 DAY) AS next_week; -- 输出: 2023-10-22 -- SELECT SUBDATE('2023-10-15', INTERVAL 1 YEAR) AS last_year; -- 输出: 2022-10-15

Java 代码示例:

import java.sql.*; public class DateCalculationExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateDateCalculations() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS tasks ( id INT AUTO_INCREMENT PRIMARY KEY, task_name VARCHAR(255), start_date DATE, duration_days INT DEFAULT 1, -- 任务持续天数 due_date DATE, reminder_date DATE ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO tasks (task_name, start_date, duration_days, due_date, reminder_date) VALUES (?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "需求分析"); pstmt.setDate(2, Date.valueOf("2023-10-10")); // 开始日期 pstmt.setInt(3, 3); // 持续3天 pstmt.setDate(3, null); // 后续计算 pstmt.setDate(4, null); // 后续计算 pstmt.addBatch(); pstmt.setString(1, "系统设计"); pstmt.setDate(2, Date.valueOf("2023-10-15")); pstmt.setInt(3, 5); pstmt.setDate(3, null); pstmt.setDate(4, null); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 任务数据已插入。"); } // 演示日期计算 String querySQL = """ SELECT task_name, start_date, duration_days, DATE_ADD(start_date, INTERVAL duration_days DAY) AS calculated_due_date, -- 计算截止日期 DATE_SUB(DATE_ADD(start_date, INTERVAL duration_days DAY), INTERVAL 1 DAY) AS actual_due_date, -- 实际截止日期(比计算的少一天) DATE_ADD(start_date, INTERVAL 1 WEEK) AS one_week_later, -- 一周后 DATE_SUB(start_date, INTERVAL 1 MONTH) AS one_month_ago, -- 一个月前 DATE_ADD(start_date, INTERVAL 1 YEAR) AS one_year_later -- 一年后 FROM tasks """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 日期计算函数演示 ==="); System.out.printf("%-15s %-12s %-15s %-20s %-20s %-20s %-20s %-20s%n", "任务名称", "开始日期", "持续天数", "计算截止日期", "实际截止日期", "一周后", "一个月前", "一年后"); while (rs.next()) { String taskName = rs.getString("task_name"); Date startDate = rs.getDate("start_date"); int durationDays = rs.getInt("duration_days"); Date calculatedDueDate = rs.getDate("calculated_due_date"); Date actualDueDate = rs.getDate("actual_due_date"); Date oneWeekLater = rs.getDate("one_week_later"); Date oneMonthAgo = rs.getDate("one_month_ago"); Date oneYearLater = rs.getDate("one_year_later"); System.out.printf("%-15s %-12s %-15d %-20s %-20s %-20s %-20s %-20s%n", taskName, startDate, durationDays, calculatedDueDate, actualDueDate, oneWeekLater, oneMonthAgo, oneYearLater); } } } catch (SQLException e) { System.err.println("执行日期计算函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateDateCalculations(); } }

代码解释

    创建表:创建 tasks 表存储任务信息。插入数据:插入两条任务记录,包含任务名称、开始日期和持续天数。查询与演示使用 DATE_ADD(start_date, INTERVAL duration_days DAY) 计算任务的预计截止日期。使用 DATE_SUB(calculated_due_date, INTERVAL 1 DAY) 计算实际截止日期(比预计少一天)。使用 DATE_ADD(start_date, INTERVAL 1 WEEK) 计算一周后的时间。使用 DATE_SUB(start_date, INTERVAL 1 MONTH) 计算一个月前的时间。使用 DATE_ADD(start_date, INTERVAL 1 YEAR) 计算一年后的时间。输出结果:展示了原始数据和各种日期计算的结果。

4. 日期差函数DATEDIFF()和TIMESTAMPDIFF()

这些函数用于计算两个日期之间的差值。

DATEDIFF(date1, date2):计算 date2date1 之间的天数差。如果 date2 大于 date1,返回正值;反之返回负值。TIMESTAMPDIFF(unit, datetime1, datetime2):计算 datetime2datetime1 之间的差值,单位由 unit 指定(如 SECOND, MINUTE, HOUR, DAY, MONTH, YEAR)。

示例:

-- SELECT DATEDIFF('2023-10-20', '2023-10-15') AS diff_days; -- 输出: 5 -- SELECT DATEDIFF('2023-10-15', '2023-10-20') AS diff_days; -- 输出: -5 -- SELECT TIMESTAMPDIFF(HOUR, '2023-10-15 10:00:00', '2023-10-15 14:30:00') AS diff_hours; -- 输出: 4 -- SELECT TIMESTAMPDIFF(MINUTE, '2023-10-15 10:00:00', '2023-10-15 10:45:30') AS diff_minutes; -- 输出: 45

Java 代码示例:

import java.sql.*; public class DateDifferenceExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateDateDifferences() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS project_milestones ( id INT AUTO_INCREMENT PRIMARY KEY, milestone_name VARCHAR(255), planned_date DATE, actual_date DATE, status ENUM('Planned', 'Completed', 'Delayed') ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO project_milestones (milestone_name, planned_date, actual_date, status) VALUES (?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "需求评审"); pstmt.setDate(2, Date.valueOf("2023-10-10")); pstmt.setDate(3, Date.valueOf("2023-10-12")); // 实际完成日期 pstmt.setString(4, "Completed"); pstmt.addBatch(); pstmt.setString(1, "原型设计"); pstmt.setDate(2, Date.valueOf("2023-10-15")); pstmt.setDate(3, Date.valueOf("2023-10-20")); // 实际完成日期 pstmt.setString(4, "Delayed"); pstmt.addBatch(); pstmt.setString(1, "代码实现"); pstmt.setDate(2, Date.valueOf("2023-10-25")); pstmt.setDate(3, null); // 尚未完成 pstmt.setString(4, "Planned"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 项目里程碑数据已插入。"); } // 演示日期差计算 String querySQL = """ SELECT milestone_name, planned_date, actual_date, DATEDIFF(actual_date, planned_date) AS days_difference, -- 计算实际日期与计划日期的天数差 CASE WHEN DATEDIFF(actual_date, planned_date) > 0 THEN '延迟' WHEN DATEDIFF(actual_date, planned_date) = 0 THEN '按时' ELSE '提前' END AS status_description, TIMESTAMPDIFF(DAY, planned_date, actual_date) AS diff_in_days, -- 使用 TIMESTAMPDIFF 计算天数差 TIMESTAMPDIFF(WEEK, planned_date, actual_date) AS diff_in_weeks, -- 计算周数差 TIMESTAMPDIFF(HOUR, planned_date, actual_date) AS diff_in_hours -- 计算小时差 FROM project_milestones WHERE actual_date IS NOT NULL -- 只计算已完成的里程碑 """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 日期差函数演示 ==="); System.out.printf("%-15s %-12s %-12s %-15s %-20s %-15s %-15s %-15s%n", "里程碑名称", "计划日期", "实际日期", "天数差", "状态描述", "天数差(新)", "周数差", "小时差"); while (rs.next()) { String milestoneName = rs.getString("milestone_name"); Date plannedDate = rs.getDate("planned_date"); Date actualDate = rs.getDate("actual_date"); int daysDifference = rs.getInt("days_difference"); String statusDescription = rs.getString("status_description"); long diffInDays = rs.getLong("diff_in_days"); long diffInWeeks = rs.getLong("diff_in_weeks"); long diffInHours = rs.getLong("diff_in_hours"); System.out.printf("%-15s %-12s %-12s %-15d %-20s %-15d %-15d %-15d%n", milestoneName, plannedDate, actualDate, daysDifference, statusDescription, diffInDays, diffInWeeks, diffInHours); } } } catch (SQLException e) { System.err.println("执行日期差函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateDateDifferences(); } }

代码解释

    创建表:创建 project_milestones 表存储项目里程碑信息。插入数据:插入三条里程碑记录,包含名称、计划日期、实际日期和状态。查询与演示使用 DATEDIFF(actual_date, planned_date) 计算实际完成日期与计划日期的天数差。使用 CASE 语句根据天数差判断项目是“提前”、“按时”还是“延迟”。使用 TIMESTAMPDIFF(DAY, planned_date, actual_date) 重复计算天数差。使用 TIMESTAMPDIFF(WEEK, planned_date, actual_date) 计算周数差。使用 TIMESTAMPDIFF(HOUR, planned_date, actual_date) 计算小时差。输出结果:展示了每个已完成里程碑的详细日期差信息。

5. 日期格式化函数DATE_FORMAT()

DATE_FORMAT(date, format) 用于将日期或时间值按照指定的格式进行格式化输出。

示例:

-- SELECT DATE_FORMAT('2023-10-15', '%Y-%m-%d') AS formatted_date; -- 输出: 2023-10-15 -- SELECT DATE_FORMAT('2023-10-15 14:30:45', '%Y/%m/%d %H:%i:%s') AS formatted_datetime; -- 输出: 2023/10/15 14:30:45 -- SELECT DATE_FORMAT('2023-10-15', '%M %D, %Y') AS formatted_long_date; -- 输出: October 15th, 2023 -- SELECT DATE_FORMAT('2023-10-15 14:30:45', '%W, %M %e, %Y at %h:%i %p') AS formatted_long_datetime; -- 输出: Sunday, October 15, 2023 at 02:30 PM

Java 代码示例:

import java.sql.*; public class DateFormattingExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateDateFormatting() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS blog_posts ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), publish_date DATETIME, category VARCHAR(100) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO blog_posts (title, publish_date, category) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "MySQL 教程入门"); pstmt.setTimestamp(2, Timestamp.valueOf("2023-10-10 09:00:00")); pstmt.setString(3, "Database"); pstmt.addBatch(); pstmt.setString(1, "Java 编程技巧"); pstmt.setTimestamp(2, Timestamp.valueOf("2023-10-12 16:30:00")); pstmt.setString(3, "Programming"); pstmt.addBatch(); pstmt.setString(1, "前端开发指南"); pstmt.setTimestamp(2, Timestamp.valueOf("2023-10-15 11:15:00")); pstmt.setString(3, "Web"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 博客文章数据已插入。"); } // 演示日期格式化 String querySQL = """ SELECT title, publish_date, DATE_FORMAT(publish_date, '%Y-%m-%d') AS simple_date, -- 简单日期格式 DATE_FORMAT(publish_date, '%Y/%m/%d %H:%i:%s') AS full_datetime, -- 完整日期时间格式 DATE_FORMAT(publish_date, '%M %D, %Y') AS long_date, -- 长日期格式 DATE_FORMAT(publish_date, '%W, %M %e, %Y at %h:%i %p') AS formatted_post_date, -- 博客文章格式 DATE_FORMAT(publish_date, '%Y年%m月%d日') AS chinese_date_format -- 中文日期格式 FROM blog_posts ORDER BY publish_date DESC """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 日期格式化函数演示 ==="); System.out.printf("%-25s %-20s %-15s %-20s %-20s %-40s %-20s%n", "文章标题", "发布日期", "简单日期", "完整时间", "长日期", "格式化发布日期", "中文日期"); while (rs.next()) { String title = rs.getString("title"); Timestamp publishDate = rs.getTimestamp("publish_date"); String simpleDate = rs.getString("simple_date"); String fullDatetime = rs.getString("full_datetime"); String longDate = rs.getString("long_date"); String formattedPostDate = rs.getString("formatted_post_date"); String chineseDateFormat = rs.getString("chinese_date_format"); System.out.printf("%-25s %-20s %-15s %-20s %-20s %-40s %-20s%n", title, publishDate, simpleDate, fullDatetime, longDate, formattedPostDate, chineseDateFormat); } } } catch (SQLException e) { System.err.println("执行日期格式化函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateDateFormatting(); } }

代码解释

    创建表:创建 blog_posts 表存储博客文章信息。插入数据:插入三条包含文章标题、发布日期和分类的博客文章记录。查询与演示使用 DATE_FORMAT(publish_date, '%Y-%m-%d') 输出简单的日期格式。使用 DATE_FORMAT(publish_date, '%Y/%m/%d %H:%i:%s') 输出完整的日期时间格式。使用 DATE_FORMAT(publish_date, '%M %D, %Y') 输出长日期格式(如 “October 15th, 2023”)。使用 DATE_FORMAT(publish_date, '%W, %M %e, %Y at %h:%i %p') 输出适合博客文章的格式(如 “Sunday, October 15, 2023 at 02:30 PM”)。使用 DATE_FORMAT(publish_date, '%Y年%m月%d日') 输出中文日期格式。输出结果:展示了每篇文章的原始发布日期和各种格式化后的日期显示效果。

三、聚合函数

聚合函数是对一组值进行计算并返回单一值的函数。它们常用于 GROUP BY 子句中进行统计分析。常见的聚合函数包括 COUNT(), SUM(), AVG(), MAX(), MIN() 等。

1. 计数函数COUNT()

COUNT() 用于计算行数或非空值的数量。

COUNT(*):计算所有行数,包括 NULL 值。COUNT(column):计算指定列中非 NULL 值的数量。COUNT(DISTINCT column):计算指定列中不同非 NULL 值的数量。

示例:

-- SELECT COUNT(*) FROM users; -- 计算用户总数 -- SELECT COUNT(email) FROM users; -- 计算有邮箱的用户数 -- SELECT COUNT(DISTINCT department) FROM employees; -- 计算不同部门的数量

Java 代码示例:

import java.sql.*; public class CountFunctionExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateCountFunction() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS sales ( id INT AUTO_INCREMENT PRIMARY KEY, product_name VARCHAR(255), quantity INT, price DECIMAL(10, 2), sale_date DATE, region VARCHAR(50) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO sales (product_name, quantity, price, sale_date, region) VALUES (?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "iPhone 14"); pstmt.setInt(2, 2); pstmt.setBigDecimal(3, new java.math.BigDecimal("999.99")); pstmt.setDate(4, Date.valueOf("2023-10-10")); pstmt.setString(5, "North"); pstmt.addBatch(); pstmt.setString(1, "Samsung Galaxy S23"); pstmt.setInt(2, 1); pstmt.setBigDecimal(3, new java.math.BigDecimal("899.99")); pstmt.setDate(4, Date.valueOf("2023-10-11")); pstmt.setString(5, "South"); pstmt.addBatch(); pstmt.setString(1, "MacBook Pro"); pstmt.setInt(2, 1); pstmt.setBigDecimal(3, new java.math.BigDecimal("1999.99")); pstmt.setDate(4, Date.valueOf("2023-10-12")); pstmt.setString(5, "North"); pstmt.addBatch(); pstmt.setString(1, "iPad Air"); pstmt.setInt(2, 3); pstmt.setBigDecimal(3, new java.math.BigDecimal("599.99")); pstmt.setDate(4, Date.valueOf("2023-10-13")); pstmt.setString(5, "East"); pstmt.addBatch(); pstmt.setString(1, "Apple Watch"); pstmt.setInt(2, 2); pstmt.setBigDecimal(3, new java.math.BigDecimal("399.99")); pstmt.setDate(4, Date.valueOf("2023-10-14")); pstmt.setString(5, "West"); pstmt.addBatch(); pstmt.setString(1, "AirPods Pro"); // 没有销售日期 pstmt.setInt(2, 1); pstmt.setBigDecimal(3, new java.math.BigDecimal("249.99")); pstmt.setDate(4, null); pstmt.setString(5, "North"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 销售数据已插入。"); } // 演示计数函数 String querySQL = """ SELECT COUNT(*) AS total_sales, COUNT(sale_date) AS sales_with_date, COUNT(DISTINCT region) AS unique_regions, COUNT(DISTINCT product_name) AS unique_products_sold FROM sales """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 计数函数演示 ==="); while (rs.next()) { long totalSales = rs.getLong("total_sales"); long salesWithDate = rs.getLong("sales_with_date"); long uniqueRegions = rs.getLong("unique_regions"); long uniqueProductsSold = rs.getLong("unique_products_sold"); System.out.println("总销售额: " + totalSales); System.out.println("有销售日期的销售额: " + salesWithDate); System.out.println("不同区域数量: " + uniqueRegions); System.out.println("不同产品数量: " + uniqueProductsSold); } } } catch (SQLException e) { System.err.println("执行计数函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateCountFunction(); } }

代码解释

    创建表:创建 sales 表存储销售记录。插入数据:插入六条销售记录,包含产品名、数量、价格、销售日期和区域。其中一条记录的销售日期为空。查询与演示使用 COUNT(*) 计算所有销售记录的数量。使用 COUNT(sale_date) 计算具有销售日期的记录数量(即 sale_date 不为 NULL 的记录数)。使用 COUNT(DISTINCT region) 计算不同区域的数量。使用 COUNT(DISTINCT product_name) 计算不同产品的数量。输出结果:展示了各种计数的结果。

2. 求和函数SUM()

SUM() 用于计算数值列的总和。

示例:

-- SELECT SUM(price) FROM sales; -- 计算所有商品的总价 -- SELECT SUM(quantity * price) FROM sales; -- 计算所有商品的总销售额 -- SELECT SUM(price) FROM sales WHERE region = 'North'; -- 计算北部区域的总销售额

Java 代码示例:

import java.sql.*; public class SumFunctionExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateSumFunction() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS inventory ( id INT AUTO_INCREMENT PRIMARY KEY, item_name VARCHAR(255), stock_quantity INT, unit_price DECIMAL(10, 2), supplier VARCHAR(100) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO inventory (item_name, stock_quantity, unit_price, supplier) VALUES (?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "笔记本电脑"); pstmt.setInt(2, 50); pstmt.setBigDecimal(3, new java.math.BigDecimal("5999.99")); pstmt.setString(4, "供应商A"); pstmt.addBatch(); pstmt.setString(1, "台式机"); pstmt.setInt(2, 30); pstmt.setBigDecimal(3, new java.math.BigDecimal("3999.99")); pstmt.setString(4, "供应商B"); pstmt.addBatch(); pstmt.setString(1, "显示器"); pstmt.setInt(2, 100); pstmt.setBigDecimal(3, new java.math.BigDecimal("1299.99")); pstmt.setString(4, "供应商A"); pstmt.addBatch(); pstmt.setString(1, "键盘"); pstmt.setInt(2, 200); pstmt.setBigDecimal(3, new java.math.BigDecimal("199.99")); pstmt.setString(4, "供应商C"); pstmt.addBatch(); pstmt.setString(1, "鼠标"); pstmt.setInt(2, 150); pstmt.setBigDecimal(3, new java.math.BigDecimal("99.99")); pstmt.setString(4, "供应商B"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 库存数据已插入。"); } // 演示求和函数 String querySQL = """ SELECT SUM(stock_quantity) AS total_stock, SUM(stock_quantity * unit_price) AS total_inventory_value, SUM(CASE WHEN supplier = '供应商A' THEN stock_quantity * unit_price ELSE 0 END) AS supplier_a_value, SUM(CASE WHEN supplier = '供应商B' THEN stock_quantity * unit_price ELSE 0 END) AS supplier_b_value FROM inventory """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 求和函数演示 ==="); while (rs.next()) { long totalStock = rs.getLong("total_stock"); BigDecimal totalInventoryValue = rs.getBigDecimal("total_inventory_value"); BigDecimal supplierAValue = rs.getBigDecimal("supplier_a_value"); BigDecimal supplierBValue = rs.getBigDecimal("supplier_b_value"); System.out.println("总库存数量: " + totalStock); System.out.println("总库存价值: ¥" + totalInventoryValue.setScale(2, java.math.RoundingMode.HALF_UP)); System.out.println("供应商A库存价值: ¥" + supplierAValue.setScale(2, java.math.RoundingMode.HALF_UP)); System.out.println("供应商B库存价值: ¥" + supplierBValue.setScale(2, java.math.RoundingMode.HALF_UP)); } } } catch (SQLException e) { System.err.println("执行求和函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateSumFunction(); } }

代码解释

    创建表:创建 inventory 表存储库存信息。插入数据:插入五条包含商品名、库存数量、单价和供应商的库存记录。查询与演示使用 SUM(stock_quantity) 计算所有商品的总库存数量。使用 SUM(stock_quantity * unit_price) 计算所有商品的总库存价值。使用 SUM(CASE WHEN supplier = '供应商A' THEN stock_quantity * unit_price ELSE 0 END) 计算供应商 A 的库存价值。使用 SUM(CASE WHEN supplier = '供应商B' THEN stock_quantity * unit_price ELSE 0 END) 计算供应商 B 的库存价值。输出结果:展示了库存总量、总价值以及按供应商划分的价值。

3. 平均值函数AVG()

AVG() 用于计算数值列的平均值。

示例:

-- SELECT AVG(price) FROM sales; -- 计算商品的平均价格 -- SELECT AVG(quantity) FROM sales; -- 计算销售数量的平均值 -- SELECT AVG(price) FROM sales WHERE region = 'North'; -- 计算北部区域商品的平均价格

Java 代码示例:

import java.sql.*; public class AvgFunctionExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateAvgFunction() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS student_scores ( id INT AUTO_INCREMENT PRIMARY KEY, student_name VARCHAR(100), subject VARCHAR(50), score DECIMAL(5, 2) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO student_scores (student_name, subject, score) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "张三"); pstmt.setString(2, "数学"); pstmt.setBigDecimal(3, new java.math.BigDecimal("85.5")); pstmt.addBatch(); pstmt.setString(1, "张三"); pstmt.setString(2, "英语"); pstmt.setBigDecimal(3, new java.math.BigDecimal("92.0")); pstmt.addBatch(); pstmt.setString(1, "张三"); pstmt.setString(2, "物理"); pstmt.setBigDecimal(3, new java.math.BigDecimal("78.5")); pstmt.addBatch(); pstmt.setString(1, "李四"); pstmt.setString(2, "数学"); pstmt.setBigDecimal(3, new java.math.BigDecimal("90.0")); pstmt.addBatch(); pstmt.setString(1, "李四"); pstmt.setString(2, "英语"); pstmt.setBigDecimal(3, new java.math.BigDecimal("88.5")); pstmt.addBatch(); pstmt.setString(1, "李四"); pstmt.setString(2, "物理"); pstmt.setBigDecimal(3, new java.math.BigDecimal("95.0")); pstmt.addBatch(); pstmt.setString(1, "王五"); pstmt.setString(2, "数学"); pstmt.setBigDecimal(3, new java.math.BigDecimal("76.0")); pstmt.addBatch(); pstmt.setString(1, "王五"); pstmt.setString(2, "英语"); pstmt.setBigDecimal(3, new java.math.BigDecimal("82.5")); pstmt.addBatch(); pstmt.setString(1, "王五"); pstmt.setString(2, "物理"); pstmt.setBigDecimal(3, new java.math.BigDecimal("80.0")); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 学生成绩数据已插入。"); } // 演示平均值函数 String querySQL = """ SELECT subject, AVG(score) AS avg_score, COUNT(*) AS student_count, ROUND(AVG(score), 2) AS rounded_avg_score -- 四舍五入到两位小数 FROM student_scores GROUP BY subject ORDER BY avg_score DESC """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 平均值函数演示 ==="); System.out.printf("%-10s %-15s %-15s %-15s%n", "科目", "平均分", "学生人数", "四舍五入平均分"); while (rs.next()) { String subject = rs.getString("subject"); BigDecimal avgScore = rs.getBigDecimal("avg_score"); long studentCount = rs.getLong("student_count"); BigDecimal roundedAvgScore = rs.getBigDecimal("rounded_avg_score"); System.out.printf("%-10s %-15s %-15d %-15s%n", subject, avgScore, studentCount, roundedAvgScore); } } } catch (SQLException e) { System.err.println("执行平均值函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateAvgFunction(); } }

代码解释

    创建表:创建 student_scores 表存储学生成绩信息。插入数据:插入九条成绩记录,每个学生三门科目的成绩。查询与演示使用 GROUP BY subject 按科目分组。使用 AVG(score) 计算每个科目的平均分。使用 COUNT(*) 计算每个科目的学生人数。使用 ROUND(AVG(score), 2) 将平均分四舍五入到两位小数。输出结果:展示了各科目的平均分、学生人数和四舍五入后的平均分,并按平均分降序排列。

4. 最大值和最小值函数MAX()和MIN()

MAX()MIN() 用于找出列中的最大值和最小值。

示例:

-- SELECT MAX(price) FROM sales; -- 找出最贵的商品价格 -- SELECT MIN(price) FROM sales; -- 找出最便宜的商品价格 -- SELECT MAX(sale_date) FROM sales; -- 找出最新的销售日期 -- SELECT MIN(sale_date) FROM sales; -- 找出最早的销售日期

Java 代码示例:

import java.sql.*; public class MaxMinFunctionExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateMaxMinFunctions() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS employee_salaries ( id INT AUTO_INCREMENT PRIMARY KEY, employee_name VARCHAR(100), department VARCHAR(50), salary DECIMAL(10, 2), hire_date DATE ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO employee_salaries (employee_name, department, salary, hire_date) VALUES (?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { pstmt.setString(1, "张经理"); pstmt.setString(2, "IT"); pstmt.setBigDecimal(3, new java.math.BigDecimal("15000.00")); pstmt.setDate(4, Date.valueOf("2020-05-10")); pstmt.addBatch(); pstmt.setString(1, "李主管"); pstmt.setString(2, "HR"); pstmt.setBigDecimal(3, new java.math.BigDecimal("12000.00")); pstmt.setDate(4, Date.valueOf("2019-08-20")); pstmt.addBatch(); pstmt.setString(1, "王专员"); pstmt.setString(2, "IT"); pstmt.setBigDecimal(3, new java.math.BigDecimal("8000.00")); pstmt.setDate(4, Date.valueOf("2021-01-15")); pstmt.addBatch(); pstmt.setString(1, "赵专员"); pstmt.setString(2, "Finance"); pstmt.setBigDecimal(3, new java.math.BigDecimal("9500.00")); pstmt.setDate(4, Date.valueOf("2020-11-30")); pstmt.addBatch(); pstmt.setString(1, "陈专员"); pstmt.setString(2, "Marketing"); pstmt.setBigDecimal(3, new java.math.BigDecimal("7500.00")); pstmt.setDate(4, Date.valueOf("2022-03-10")); pstmt.addBatch(); pstmt.setString(1, "孙主管"); pstmt.setString(2, "Finance"); pstmt.setBigDecimal(3, new java.math.BigDecimal("13000.00")); pstmt.setDate(4, Date.valueOf("2018-07-05")); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 员工薪资数据已插入。"); } // 演示最大值和最小值函数 String querySQL = """ SELECT MAX(salary) AS highest_salary, MIN(salary) AS lowest_salary, MAX(hire_date) AS latest_hire_date, MIN(hire_date) AS earliest_hire_date, AVG(salary) AS average_salary, (MAX(salary) - MIN(salary)) AS salary_range -- 薪资差距 FROM employee_salaries """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 最大值和最小值函数演示 ==="); while (rs.next()) { BigDecimal highestSalary = rs.getBigDecimal("highest_salary"); BigDecimal lowestSalary = rs.getBigDecimal("lowest_salary"); Date latestHireDate = rs.getDate("latest_hire_date"); Date earliestHireDate = rs.getDate("earliest_hire_date"); BigDecimal averageSalary = rs.getBigDecimal("average_salary"); BigDecimal salaryRange = rs.getBigDecimal("salary_range"); System.out.println("最高薪资: ¥" + highestSalary.setScale(2, java.math.RoundingMode.HALF_UP)); System.out.println("最低薪资: ¥" + lowestSalary.setScale(2, java.math.RoundingMode.HALF_UP)); System.out.println("最新入职日期: " + latestHireDate); System.out.println("最早入职日期: " + earliestHireDate); System.out.println("平均薪资: ¥" + averageSalary.setScale(2, java.math.RoundingMode.HALF_UP)); System.out.println("薪资差距: ¥" + salaryRange.setScale(2, java.math.RoundingMode.HALF_UP)); } } } catch (SQLException e) { System.err.println("执行最大值最小值函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateMaxMinFunctions(); } }

代码解释

    创建表:创建 employee_salaries 表存储员工薪资信息。插入数据:插入六条员工记录,包含姓名、部门、薪资和入职日期。查询与演示使用 MAX(salary) 找出最高薪资。使用 MIN(salary) 找出最低薪资。使用 MAX(hire_date) 找出最新的入职日期。使用 MIN(hire_date) 找出最早的入职日期。使用 AVG(salary) 计算平均薪资。使用 (MAX(salary) - MIN(salary)) 计算薪资差距。输出结果:展示了员工薪资和入职日期的统计信息。

5. 聚合函数与分组GROUP BY结合使用

聚合函数通常与 GROUP BY 子句一起使用,以对数据进行分组并计算每组的聚合值。

示例:

-- SELECT department, COUNT(*) FROM employees GROUP BY department; -- 按部门统计员工数量 -- SELECT department, AVG(salary) FROM employees GROUP BY department; -- 按部门计算平均薪资 -- SELECT product_category, SUM(sales_amount) FROM sales GROUP BY product_category; -- 按产品类别统计销售额

Java 代码示例:

import java.sql.*; public class GroupByAggregationExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void demonstrateGroupByAggregation() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 创建示例表 String createTableSQL = """ CREATE TABLE IF NOT EXISTS transactions ( id INT AUTO_INCREMENT PRIMARY KEY, customer_id INT, customer_name VARCHAR(100), transaction_type ENUM('Purchase', 'Refund'), amount DECIMAL(10, 2), transaction_date DATE, category VARCHAR(50) ) """; try (Statement stmt = conn.createStatement()) { stmt.execute(createTableSQL); } // 插入测试数据 String insertSQL = "INSERT INTO transactions (customer_id, customer_name, transaction_type, amount, transaction_date, category) VALUES (?, ?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { // 客户 A pstmt.setInt(1, 1); pstmt.setString(2, "张三"); pstmt.setString(3, "Purchase"); pstmt.setBigDecimal(4, new java.math.BigDecimal("150.00")); pstmt.setDate(5, Date.valueOf("2023-10-10")); pstmt.setString(6, "Electronics"); pstmt.addBatch(); pstmt.setInt(1, 1); pstmt.setString(2, "张三"); pstmt.setString(3, "Purchase"); pstmt.setBigDecimal(4, new java.math.BigDecimal("80.00")); pstmt.setDate(5, Date.valueOf("2023-10-12")); pstmt.setString(6, "Clothing"); pstmt.addBatch(); pstmt.setInt(1, 1); pstmt.setString(2, "张三"); pstmt.setString(3, "Refund"); pstmt.setBigDecimal(4, new java.math.BigDecimal("50.00")); pstmt.setDate(5, Date.valueOf("2023-10-15")); pstmt.setString(6, "Electronics"); pstmt.addBatch(); // 客户 B pstmt.setInt(1, 2); pstmt.setString(2, "李四"); pstmt.setString(3, "Purchase"); pstmt.setBigDecimal(4, new java.math.BigDecimal("200.00")); pstmt.setDate(5, Date.valueOf("2023-10-11")); pstmt.setString(6, "Books"); pstmt.addBatch(); pstmt.setInt(1, 2); pstmt.setString(2, "李四"); pstmt.setString(3, "Purchase"); pstmt.setBigDecimal(4, new java.math.BigDecimal("120.00")); pstmt.setDate(5, Date.valueOf("2023-10-13")); pstmt.setString(6, "Electronics"); pstmt.addBatch(); pstmt.setInt(1, 2); pstmt.setString(2, "李四"); pstmt.setString(3, "Purchase"); pstmt.setBigDecimal(4, new java.math.BigDecimal("30.00")); pstmt.setDate(5, Date.valueOf("2023-10-14")); pstmt.setString(6, "Food"); pstmt.addBatch(); // 客户 C pstmt.setInt(1, 3); pstmt.setString(2, "王五"); pstmt.setString(3, "Purchase"); pstmt.setBigDecimal(4, new java.math.BigDecimal("100.00")); pstmt.setDate(5, Date.valueOf("2023-10-16")); pstmt.setString(6, "Clothing"); pstmt.addBatch(); pstmt.setInt(1, 3); pstmt.setString(2, "王五"); pstmt.setString(3, "Purchase"); pstmt.setBigDecimal(4, new java.math.BigDecimal("90.00")); pstmt.setDate(5, Date.valueOf("2023-10-17")); pstmt.setString(6, "Books"); pstmt.addBatch(); pstmt.executeBatch(); System.out.println("✅ 交易数据已插入。"); } // 演示分组聚合 String querySQL = """ SELECT customer_name, COUNT(*) AS transaction_count, SUM(CASE WHEN transaction_type = 'Purchase' THEN amount ELSE 0 END) AS total_purchases, SUM(CASE WHEN transaction_type = 'Refund' THEN amount ELSE 0 END) AS total_refunds, SUM(amount) AS net_amount, AVG(amount) AS average_transaction_amount, MAX(transaction_date) AS last_transaction_date, MIN(transaction_date) AS first_transaction_date FROM transactions GROUP BY customer_name ORDER BY total_purchases DESC """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL)) { System.out.println("\n=== 分组聚合函数演示 ==="); System.out.printf("%-10s %-15s %-15s %-15s %-15s %-15s %-20s %-20s%n", "客户名", "交易次数", "总支出", "总退款", "净额", "平均交易额", "最近交易日期", "首次交易日期"); while (rs.next()) { String customerName = rs.getString("customer_name"); long transactionCount = rs.getLong("transaction_count"); BigDecimal totalPurchases = rs.getBigDecimal("total_purchases"); BigDecimal totalRefunds = rs.getBigDecimal("total_refunds"); BigDecimal netAmount = rs.getBigDecimal("net_amount"); BigDecimal averageTransactionAmount = rs.getBigDecimal("average_transaction_amount"); Date lastTransactionDate = rs.getDate("last_transaction_date"); Date firstTransactionDate = rs.getDate("first_transaction_date"); System.out.printf("%-10s %-15d %-15s %-15s %-15s %-15s %-20s %-20s%n", customerName, transactionCount, totalPurchases.setScale(2, java.math.RoundingMode.HALF_UP), totalRefunds.setScale(2, java.math.RoundingMode.HALF_UP), netAmount.setScale(2, java.math.RoundingMode.HALF_UP), averageTransactionAmount.setScale(2, java.math.RoundingMode.HALF_UP), lastTransactionDate, firstTransactionDate); } } } catch (SQLException e) { System.err.println("执行分组聚合函数演示时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { demonstrateGroupByAggregation(); } }

代码解释

    创建表:创建 transactions 表存储交易记录。插入数据:插入九条交易记录,包含客户 ID、姓名、交易类型(购买或退款)、金额、日期和类别。查询与演示使用 GROUP BY customer_name 按客户分组。使用 COUNT(*) 计算每位客户的交易次数。使用 SUM(CASE WHEN transaction_type = 'Purchase' THEN amount ELSE 0 END) 计算每位客户的总支出。使用 SUM(CASE WHEN transaction_type = 'Refund' THEN amount ELSE 0 END) 计算每位客户的总退款。使用 SUM(amount) 计算每位客户的净额(支出减去退款)。使用 AVG(amount) 计算每位客户的平均交易额。使用 MAX(transaction_date)MIN(transaction_date) 找出每位客户的最近和首次交易日期。使用 ORDER BY total_purchases DESC 按总支出降序排列。输出结果:展示了每位客户的详细交易统计信息。

四、综合实战:销售数据分析

让我们结合前面学到的所有函数,进行一次综合实战——构建一个销售数据分析报告。

实战目标

为一家零售公司生成一份销售报告,包含:

    每个产品的销售总额。每个产品的平均销售价格。每个区域的销售总额。每个季度的销售总额。每个季度的增长率。各个产品的销售排名。

数据准备

-- 创建销售数据表 CREATE TABLE IF NOT EXISTS quarterly_sales ( id INT AUTO_INCREMENT PRIMARY KEY, product_name VARCHAR(255), quantity INT, unit_price DECIMAL(10, 2), sale_date DATE, region VARCHAR(50), quarter VARCHAR(10) -- 用于存储季度信息,例如 'Q1_2023' ); -- 插入示例数据 INSERT INTO quarterly_sales (product_name, quantity, unit_price, sale_date, region, quarter) VALUES ('iPhone 14', 2, 999.99, '2023-01-15', 'North', 'Q1_2023'), ('Samsung Galaxy S23', 1, 899.99, '2023-01-20', 'South', 'Q1_2023'), ('MacBook Pro', 1, 1999.99, '2023-02-10', 'North', 'Q1_2023'), ('iPad Air', 3, 599.99, '2023-02-25', 'East', 'Q1_2023'), ('Apple Watch', 2, 399.99, '2023-03-10', 'West', 'Q1_2023'), ('AirPods Pro', 1, 249.99, '2023-03-20', 'North', 'Q1_2023'), ('Galaxy Tab S8', 2, 799.99, '2023-04-05', 'South', 'Q2_2023'), ('Surface Laptop', 1, 1299.99, '2023-04-15', 'West', 'Q2_2023'), ('ThinkPad X1', 1, 1899.99, '2023-05-10', 'North', 'Q2_2023'), ('Pixel 7', 2, 799.99, '2023-05-20', 'East', 'Q2_2023'), ('Dell XPS', 1, 1599.99, '2023-06-05', 'West', 'Q2_2023'), ('MacBook Air', 3, 1199.99, '2023-06-15', 'North', 'Q2_2023'), ('iPhone 15', 1, 1099.99, '2023-07-10', 'South', 'Q3_2023'), ('Galaxy S24', 2, 999.99, '2023-07-20', 'North', 'Q3_2023'), ('Surface Pro', 1, 1399.99, '2023-08-05', 'West', 'Q3_2023'), ('ThinkPad P1', 1, 2499.99, '2023-08-15', 'East', 'Q3_2023'), ('iPad Pro', 2, 899.99, '2023-09-05', 'North', 'Q3_2023'), ('AirPods 2nd Gen', 3, 179.99, '2023-09-20', 'South', 'Q3_2023'), ('Galaxy Watch', 1, 299.99, '2023-10-05', 'West', 'Q4_2023'), ('Mac Studio', 1, 2999.99, '2023-10-10', 'North', 'Q4_2023'), ('Surface Book', 1, 1899.99, '2023-10-20', 'East', 'Q4_2023'), ('Dell Inspiron', 2, 899.99, '2023-10-25', 'South', 'Q4_2023');

Java 代码示例

import java.sql.*; import java.math.BigDecimal; public class SalesAnalysisExample { private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; private static final String DB_USER = "root"; // 替换为你的用户名 private static final String DB_PASSWORD = "password"; // 替换为你的密码 public static void generateSalesReport() { Connection conn = null; try { conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 1. 每个产品的销售总额和平均价格 String productSalesQuery = """ SELECT product_name, SUM(quantity * unit_price) AS total_sales, AVG(unit_price) AS avg_price, SUM(quantity) AS total_quantity_sold FROM quarterly_sales GROUP BY product_name ORDER BY total_sales DESC """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(productSalesQuery)) { System.out.println("\n=== 产品销售总额与平均价格 ==="); System.out.printf("%-20s %-15s %-15s %-15s%n", "产品名", "总销售额", "平均单价", "总销量"); while (rs.next()) { String productName = rs.getString("product_name"); BigDecimal totalSales = rs.getBigDecimal("total_sales"); BigDecimal avgPrice = rs.getBigDecimal("avg_price"); long totalQuantitySold = rs.getLong("total_quantity_sold"); System.out.printf("%-20s %-15s %-15s %-15d%n", productName, totalSales.setScale(2, java.math.RoundingMode.HALF_UP), avgPrice.setScale(2, java.math.RoundingMode.HALF_UP), totalQuantitySold); } } // 2. 每个区域的销售总额 String regionSalesQuery = """ SELECT region, SUM(quantity * unit_price) AS total_sales FROM quarterly_sales GROUP BY region ORDER BY total_sales DESC """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(regionSalesQuery)) { System.out.println("\n=== 区域销售总额 ==="); System.out.printf("%-10s %-15s%n", "区域", "总销售额"); while (rs.next()) { String region = rs.getString("region"); BigDecimal totalSales = rs.getBigDecimal("total_sales"); System.out.printf("%-10s %-15s%n", region, totalSales.setScale(2, java.math.RoundingMode.HALF_UP)); } } // 3. 每个季度的销售总额 String quarterSalesQuery = """ SELECT quarter, SUM(quantity * unit_price) AS total_sales FROM quarterly_sales GROUP BY quarter ORDER BY quarter """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(quarterSalesQuery)) { System.out.println("\n=== 季度销售总额 ==="); System.out.printf("%-12s %-15s%n", "季度", "总销售额"); while (rs.next()) { String quarter = rs.getString("quarter"); BigDecimal totalSales = rs.getBigDecimal("total_sales"); System.out.printf("%-12s %-15s%n", quarter, totalSales.setScale(2, java.math.RoundingMode.HALF_UP)); } } // 4. 每个季度的增长率(使用 LAG 窗口函数) // 注意:MySQL 8.0+ 支持窗口函数 String quarterGrowthQuery = """ WITH quarterly_totals AS ( SELECT quarter, SUM(quantity * unit_price) AS total_sales FROM quarterly_sales GROUP BY quarter ), quarterly_growth AS ( SELECT quarter, total_sales, LAG(total_sales, 1) OVER (ORDER BY quarter) AS previous_quarter_sales FROM quarterly_totals ) SELECT quarter, total_sales, previous_quarter_sales, CASE WHEN previous_quarter_sales IS NOT NULL AND previous_quarter_sales > 0 THEN ROUND(((total_sales - previous_quarter_sales) / previous_quarter_sales) * 100, 2) ELSE NULL END AS growth_percentage FROM quarterly_growth ORDER BY quarter """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(quarterGrowthQuery)) { System.out.println("\n=== 季度销售增长率 ==="); System.out.printf("%-12s %-15s %-15s %-15s%n", "季度", "总销售额", "上季度销售额", "增长率(%)"); while (rs.next()) { String quarter = rs.getString("quarter"); BigDecimal totalSales = rs.getBigDecimal("total_sales"); BigDecimal previousQuarterSales = rs.getBigDecimal("previous_quarter_sales"); BigDecimal growthPercentage = rs.getBigDecimal("growth_percentage"); System.out.printf("%-12s %-15s %-15s %-15s%n", quarter, totalSales.setScale(2, java.math.RoundingMode.HALF_UP), previousQuarterSales == null ? "N/A" : previousQuarterSales.setScale(2, java.math.RoundingMode.HALF_UP), growthPercentage == null ? "N/A" : growthPercentage.toString() + "%"); } } // 5. 各个产品的销售排名 String productRankingQuery = """ SELECT product_name, SUM(quantity * unit_price) AS total_sales, ROW_NUMBER() OVER (ORDER BY SUM(quantity * unit_price) DESC) AS sales_rank FROM quarterly_sales GROUP BY product_name ORDER BY total_sales DESC """; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(productRankingQuery)) { System.out.println("\n=== 产品销售排名 ==="); System.out.printf("%-20s %-15s %-10s%n", "产品名", "总销售额", "排名"); while (rs.next()) { String productName = rs.getString("product_name"); BigDecimal totalSales = rs.getBigDecimal("total_sales"); long salesRank = rs.getLong("sales_rank"); System.out.printf("%-20s %-15s %-10d%n", productName, totalSales.setScale(2, java.math.RoundingMode.HALF_UP), salesRank); } } } catch (SQLException e) { System.err.println("生成销售报告时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { generateSalesReport(); } }

代码解释

    连接数据库:获取数据库连接。产品销售分析使用 SUM(quantity * unit_price) 计算每个产品的总销售额。使用 AVG(unit_price) 计算每个产品的平均单价。使用 SUM(quantity) 计算每个产品的总销量。使用 GROUP BY product_name 按产品分组。使用 ORDER BY total_sales DESC 按销售额降序排列。区域销售分析使用 SUM(quantity * unit_price) 计算每个区域的总销售额。使用 GROUP BY region 按区域分组。使用 ORDER BY total_sales DESC 按销售额降序排列。季度销售分析使用 SUM(quantity * unit_price) 计算每个季度的总销售额。使用 GROUP BY quarter 按季度分组。使用 ORDER BY quarter 按季度升序排列。季度增长率分析使用 WITH 子句创建一个临时结果集 quarterly_totals,计算每个季度的总销售额。使用 LAG 窗口函数获取上一季度的销售额。计算增长率:(本期销售额 - 上期销售额) / 上期销售额 * 100使用 CASE 语句处理第一个季度没有上期数据的情况。产品销售排名使用 ROW_NUMBER() OVER (ORDER BY SUM(quantity * unit_price) DESC) 为产品分配销售额排名。使用 GROUP BY product_name 按产品分组。使用 ORDER BY total_sales DESC 按销售额降序排列。输出报告:在控制台打印出所有分析结果。

总结:函数是数据库操作的利器

通过今天的深入学习,我们掌握了 MySQL 中最常用的字符串函数、日期函数和聚合函数。这些函数不仅仅是简单的工具,更是解决实际业务问题的强大武器。从简单的字符串拼接、日期计算,到复杂的统计分析,函数的应用无处不在。

字符串函数:帮助我们处理和转换文本数据,确保数据格式统一、整洁。日期函数:使我们能够高效地处理时间相关的业务逻辑,如计算周期、格式化显示等。聚合函数:是数据分析的核心,让我们能够快速洞察数据背后的规律和趋势。

在实际项目中,熟练运用这些函数,不仅能提高开发效率,还能写出更加健壮和易维护的 SQL 查询语句。希望这篇博客能为你在 MySQL 的道路上提供坚实的基础和实用的指导!

???? 了解更多关于 MySQL 函数的信息,请参考以下官方文档和权威资源:

    MySQL 8.0 Reference Manual - Functions and OperatorsMySQL 8.0 Reference Manual - String FunctionsMySQL 8.0 Reference Manual - Date and Time FunctionsMySQL 8.0 Reference Manual - Aggregate (GROUP BY) FunctionsW3Schools SQL FunctionsGeeksforGeeks - MySQL String FunctionsGeeksforGeeks - MySQL Date FunctionsGeeksforGeeks - MySQL Aggregate Functions

Mermaid 图表:字符串函数关系图 

Mermaid 图表:字符串函数关系图

Mermaid 图表:日期函数关系图 

Mermaid 图表:日期函数关系图

Mermaid 图表:聚合函数关系图 

Mermaid 图表:聚合函数关系图

Mermaid 图表:函数综合应用示意图 

Mermaid 图表:函数综合应用示意图

总结

到此这篇关于MySQL常用函数之字符串、日期、聚合函数的文章就介绍到这了,

相关推荐

热文推荐