MySQL - 30 分钟搞定常用函数:字符串、日期、聚合函数实操

MySQL - 30 分钟搞定常用函数:字符串、日期、聚合函数实操
在这里插入图片描述
👋 大家好,欢迎来到我的技术博客!
💻 作为一名热爱 Java 与软件开发的程序员,我始终相信:清晰的逻辑 + 持续的积累 = 稳健的成长
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕MySQL这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!

文章目录

MySQL - 30 分钟搞定常用函数:字符串、日期、聚合函数实操 🧠

在数据库的世界里,函数就像是程序员的工具箱,为我们提供了强大的数据处理能力。无论是清洗数据、格式化输出、计算时间差,还是进行统计分析,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 代码示例:
importjava.sql.*;importjava.util.ArrayList;importjava.util.List;publicclassStringFunctionExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateLengthFunctions(){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_LENGTHString 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();}}}publicstaticvoidmain(String[] args){demonstrateLengthFunctions();}}
代码解释
  1. 创建表:首先创建一个 sample_users 表用于存储示例数据。
  2. 插入数据:使用 PreparedStatement 批量插入包含英文和中文的测试数据。
  3. 查询与展示:执行 SQL 查询,同时使用 LENGTH()CHAR_LENGTH() 计算 description 字段的长度,并在 Java 控制台打印结果。
  4. 输出对比:对于中文字符,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 代码示例:
importjava.sql.*;publicclassSubstringExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateSubstringFunctions(){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();}}}publicstaticvoidmain(String[] args){demonstrateSubstringFunctions();}}
代码解释
  1. 创建表:创建 products 表存储产品信息。
  2. 插入数据:插入三条包含产品名称和代码的测试数据。
  3. 查询与演示
    • 使用 LEFT(product_name, 5) 提取产品名称的前 5 个字符作为品牌名。
    • 使用 RIGHT(product_code, 3) 提取产品代码的后 3 个字符。
    • 使用 SUBSTRING(product_name, 1, 5)LEFT 类似,但更灵活。
    • 使用 SUBSTRING(product_code, 4, 3) 从产品代码的第 4 个字符开始截取 3 个字符。
  4. 输出结果:清晰地展示了不同截取函数的效果。

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 代码示例:
importjava.sql.*;publicclassReplaceExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateReplaceFunction(){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,"[email protected]"); pstmt.setString(3,"我是张小明,热爱技术。联系方式:[email protected]"); pstmt.addBatch(); pstmt.setString(1,"李丽"); pstmt.setString(2,"[email protected]"); pstmt.setString(3,"李丽,软件工程师。邮箱地址:[email protected]"); 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();}}}publicstaticvoidmain(String[] args){demonstrateReplaceFunction();}}
代码解释
  1. 创建表:创建 user_profiles 表存储用户信息。
  2. 插入数据:插入包含用户姓名、邮箱和简介的测试数据,其中简介中包含了邮箱地址。
  3. 查询与演示
    • 使用 REPLACE(bio, '联系方式', '联系信息') 将简介中的“联系方式”替换为“联系信息”。
    • 使用 REPLACE(email, '@', '[AT]') 将邮箱地址中的 @ 符号替换为 [AT],用于演示(实际应用中可能用于脱敏)。
  4. 输出结果:展示原始数据和替换后的效果。

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 代码示例:
importjava.sql.*;publicclassCaseConversionExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateCaseConversion(){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();}}}publicstaticvoidmain(String[] args){demonstrateCaseConversion();}}
代码解释
  1. 创建表:创建 employees 表存储员工信息。
  2. 插入数据:插入包含员工名、姓和部门的测试数据,其中名字和姓氏大小写不统一。
  3. 查询与演示
    • 使用 UPPER(first_name) 将名字转换为大写。
    • 使用 LOWER(last_name) 将姓氏转换为小写。
    • 使用 CONCAT(UPPER(SUBSTRING(first_name, 1, 1)), LOWER(SUBSTRING(first_name, 2))) 实现简单的标题格式(首字母大写,其余小写)。
  4. 输出结果:展示了原始数据和各种大小写转换后的效果。

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 代码示例:
importjava.sql.*;publicclassTrimExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateTrimFunctions(){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();}}}publicstaticvoidmain(String[] args){demonstrateTrimFunctions();}}
代码解释
  1. 创建表:创建 user_input_logs 表用于记录用户输入。
  2. 插入数据:插入包含前后空格、制表符、换行符的原始数据。
  3. 查询与演示
    • 使用 TRIM(raw_data) 去除两端的空白字符。
    • 使用 LTRIM(raw_data) 去除左侧空白。
    • 使用 RTRIM(raw_data) 去除右侧空白。
    • 使用 TRIM('\r\n\t ' FROM raw_data) 去除指定的多种空白字符。
  4. 输出结果:展示了原始数据和各种去空格处理后的效果。

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 代码示例:
importjava.sql.*;publicclassConcatExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateConcatFunction(){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();}}}publicstaticvoidmain(String[] args){demonstrateConcatFunction();}}
代码解释
  1. 创建表:创建 addresses 表存储地址信息。
  2. 插入数据:插入包含街道、城市、州、邮编的测试数据。
  3. 查询与演示
    • 使用 CONCAT(street, ', ', city, ', ', state, ' ', zip_code) 将地址信息拼接成一个完整的地址字符串。
    • 使用 CONCAT_WS(', ', street, city, state, zip_code) 使用逗号作为分隔符拼接地址,CONCAT_WSCONCAT 的变体,专门用于带分隔符的连接。
  4. 输出结果:展示了原始数据和拼接后的地址效果。

二、日期函数 📅

日期函数是处理时间相关数据的强大工具。在应用开发中,我们经常需要计算日期差、提取日期组件、格式化日期显示、处理时间戳等。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 代码示例:
importjava.sql.*;importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;publicclassDateTimeFunctionExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateDateTimeFunctions(){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();}}}publicstaticvoidmain(String[] args){demonstrateDateTimeFunctions();}}
代码解释
  1. 创建表:创建 events 表存储事件信息,包含日期和时间字段。
  2. 插入数据:插入两条事件记录,包含事件名称、日期和创建时间。
  3. 查询与演示
    • 使用 NOW() 获取数据库服务器的当前时间。
    • 使用 CURDATE()CURTIME() 获取当前日期和时间。
    • 使用 DATE(created_at)DATETIME 字段中提取日期部分。
    • 使用 TIME(created_at)DATETIME 字段中提取时间部分。
    • 使用 DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') 格式化时间显示。
  4. 输出结果:展示了原始数据和各种日期时间函数的处理结果。

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 代码示例:
importjava.sql.*;publicclassExtractDateTimeComponentsExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateExtractDateTimeComponents(){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();}}}publicstaticvoidmain(String[] args){demonstrateExtractDateTimeComponents();}}
代码解释
  1. 创建表:创建 orders 表存储订单信息。
  2. 插入数据:插入三条包含客户名和下单时间的订单记录。
  3. 查询与演示
    • 使用 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 确保月份是两位数)。
  4. 输出结果:展示了原始时间数据和各个组件的提取结果。

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 代码示例:
importjava.sql.*;publicclassDateCalculationExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateDateCalculations(){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();}}}publicstaticvoidmain(String[] args){demonstrateDateCalculations();}}
代码解释
  1. 创建表:创建 tasks 表存储任务信息。
  2. 插入数据:插入两条任务记录,包含任务名称、开始日期和持续天数。
  3. 查询与演示
    • 使用 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. 输出结果:展示了原始数据和各种日期计算的结果。

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 代码示例:
importjava.sql.*;publicclassDateDifferenceExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateDateDifferences(){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();}}}publicstaticvoidmain(String[] args){demonstrateDateDifferences();}}
代码解释
  1. 创建表:创建 project_milestones 表存储项目里程碑信息。
  2. 插入数据:插入三条里程碑记录,包含名称、计划日期、实际日期和状态。
  3. 查询与演示
    • 使用 DATEDIFF(actual_date, planned_date) 计算实际完成日期与计划日期的天数差。
    • 使用 CASE 语句根据天数差判断项目是“提前”、“按时”还是“延迟”。
    • 使用 TIMESTAMPDIFF(DAY, planned_date, actual_date) 重复计算天数差。
    • 使用 TIMESTAMPDIFF(WEEK, planned_date, actual_date) 计算周数差。
    • 使用 TIMESTAMPDIFF(HOUR, planned_date, actual_date) 计算小时差。
  4. 输出结果:展示了每个已完成里程碑的详细日期差信息。

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 代码示例:
importjava.sql.*;publicclassDateFormattingExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateDateFormatting(){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();}}}publicstaticvoidmain(String[] args){demonstrateDateFormatting();}}
代码解释
  1. 创建表:创建 blog_posts 表存储博客文章信息。
  2. 插入数据:插入三条包含文章标题、发布日期和分类的博客文章记录。
  3. 查询与演示
    • 使用 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日') 输出中文日期格式。
  4. 输出结果:展示了每篇文章的原始发布日期和各种格式化后的日期显示效果。

三、聚合函数 📊

聚合函数是对一组值进行计算并返回单一值的函数。它们常用于 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 代码示例:
importjava.sql.*;publicclassCountFunctionExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateCountFunction(){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,newjava.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,newjava.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,newjava.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,newjava.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,newjava.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,newjava.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();}}}publicstaticvoidmain(String[] args){demonstrateCountFunction();}}
代码解释
  1. 创建表:创建 sales 表存储销售记录。
  2. 插入数据:插入六条销售记录,包含产品名、数量、价格、销售日期和区域。其中一条记录的销售日期为空。
  3. 查询与演示
    • 使用 COUNT(*) 计算所有销售记录的数量。
    • 使用 COUNT(sale_date) 计算具有销售日期的记录数量(即 sale_date 不为 NULL 的记录数)。
    • 使用 COUNT(DISTINCT region) 计算不同区域的数量。
    • 使用 COUNT(DISTINCT product_name) 计算不同产品的数量。
  4. 输出结果:展示了各种计数的结果。

2. 求和函数 SUM()

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

示例:
-- SELECT SUM(price) FROM sales; -- 计算所有商品的总价-- SELECT SUM(quantity * price) FROM sales; -- 计算所有商品的总销售额-- SELECT SUM(price) FROM sales WHERE region = 'North'; -- 计算北部区域的总销售额
Java 代码示例:
importjava.sql.*;publicclassSumFunctionExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateSumFunction(){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,newjava.math.BigDecimal("5999.99")); pstmt.setString(4,"供应商A"); pstmt.addBatch(); pstmt.setString(1,"台式机"); pstmt.setInt(2,30); pstmt.setBigDecimal(3,newjava.math.BigDecimal("3999.99")); pstmt.setString(4,"供应商B"); pstmt.addBatch(); pstmt.setString(1,"显示器"); pstmt.setInt(2,100); pstmt.setBigDecimal(3,newjava.math.BigDecimal("1299.99")); pstmt.setString(4,"供应商A"); pstmt.addBatch(); pstmt.setString(1,"键盘"); pstmt.setInt(2,200); pstmt.setBigDecimal(3,newjava.math.BigDecimal("199.99")); pstmt.setString(4,"供应商C"); pstmt.addBatch(); pstmt.setString(1,"鼠标"); pstmt.setInt(2,150); pstmt.setBigDecimal(3,newjava.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();}}}publicstaticvoidmain(String[] args){demonstrateSumFunction();}}
代码解释
  1. 创建表:创建 inventory 表存储库存信息。
  2. 插入数据:插入五条包含商品名、库存数量、单价和供应商的库存记录。
  3. 查询与演示
    • 使用 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 的库存价值。
  4. 输出结果:展示了库存总量、总价值以及按供应商划分的价值。

3. 平均值函数 AVG()

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

示例:
-- SELECT AVG(price) FROM sales; -- 计算商品的平均价格-- SELECT AVG(quantity) FROM sales; -- 计算销售数量的平均值-- SELECT AVG(price) FROM sales WHERE region = 'North'; -- 计算北部区域商品的平均价格
Java 代码示例:
importjava.sql.*;publicclassAvgFunctionExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateAvgFunction(){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,newjava.math.BigDecimal("85.5")); pstmt.addBatch(); pstmt.setString(1,"张三"); pstmt.setString(2,"英语"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("92.0")); pstmt.addBatch(); pstmt.setString(1,"张三"); pstmt.setString(2,"物理"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("78.5")); pstmt.addBatch(); pstmt.setString(1,"李四"); pstmt.setString(2,"数学"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("90.0")); pstmt.addBatch(); pstmt.setString(1,"李四"); pstmt.setString(2,"英语"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("88.5")); pstmt.addBatch(); pstmt.setString(1,"李四"); pstmt.setString(2,"物理"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("95.0")); pstmt.addBatch(); pstmt.setString(1,"王五"); pstmt.setString(2,"数学"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("76.0")); pstmt.addBatch(); pstmt.setString(1,"王五"); pstmt.setString(2,"英语"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("82.5")); pstmt.addBatch(); pstmt.setString(1,"王五"); pstmt.setString(2,"物理"); pstmt.setBigDecimal(3,newjava.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();}}}publicstaticvoidmain(String[] args){demonstrateAvgFunction();}}
代码解释
  1. 创建表:创建 student_scores 表存储学生成绩信息。
  2. 插入数据:插入九条成绩记录,每个学生三门科目的成绩。
  3. 查询与演示
    • 使用 GROUP BY subject 按科目分组。
    • 使用 AVG(score) 计算每个科目的平均分。
    • 使用 COUNT(*) 计算每个科目的学生人数。
    • 使用 ROUND(AVG(score), 2) 将平均分四舍五入到两位小数。
  4. 输出结果:展示了各科目的平均分、学生人数和四舍五入后的平均分,并按平均分降序排列。

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 代码示例:
importjava.sql.*;publicclassMaxMinFunctionExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateMaxMinFunctions(){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,newjava.math.BigDecimal("15000.00")); pstmt.setDate(4,Date.valueOf("2020-05-10")); pstmt.addBatch(); pstmt.setString(1,"李主管"); pstmt.setString(2,"HR"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("12000.00")); pstmt.setDate(4,Date.valueOf("2019-08-20")); pstmt.addBatch(); pstmt.setString(1,"王专员"); pstmt.setString(2,"IT"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("8000.00")); pstmt.setDate(4,Date.valueOf("2021-01-15")); pstmt.addBatch(); pstmt.setString(1,"赵专员"); pstmt.setString(2,"Finance"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("9500.00")); pstmt.setDate(4,Date.valueOf("2020-11-30")); pstmt.addBatch(); pstmt.setString(1,"陈专员"); pstmt.setString(2,"Marketing"); pstmt.setBigDecimal(3,newjava.math.BigDecimal("7500.00")); pstmt.setDate(4,Date.valueOf("2022-03-10")); pstmt.addBatch(); pstmt.setString(1,"孙主管"); pstmt.setString(2,"Finance"); pstmt.setBigDecimal(3,newjava.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();}}}publicstaticvoidmain(String[] args){demonstrateMaxMinFunctions();}}
代码解释
  1. 创建表:创建 employee_salaries 表存储员工薪资信息。
  2. 插入数据:插入六条员工记录,包含姓名、部门、薪资和入职日期。
  3. 查询与演示
    • 使用 MAX(salary) 找出最高薪资。
    • 使用 MIN(salary) 找出最低薪资。
    • 使用 MAX(hire_date) 找出最新的入职日期。
    • 使用 MIN(hire_date) 找出最早的入职日期。
    • 使用 AVG(salary) 计算平均薪资。
    • 使用 (MAX(salary) - MIN(salary)) 计算薪资差距。
  4. 输出结果:展示了员工薪资和入职日期的统计信息。

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 代码示例:
importjava.sql.*;publicclassGroupByAggregationExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoiddemonstrateGroupByAggregation(){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,newjava.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,newjava.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,newjava.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,newjava.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,newjava.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,newjava.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,newjava.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,newjava.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();}}}publicstaticvoidmain(String[] args){demonstrateGroupByAggregation();}}
代码解释
  1. 创建表:创建 transactions 表存储交易记录。
  2. 插入数据:插入九条交易记录,包含客户 ID、姓名、交易类型(购买或退款)、金额、日期和类别。
  3. 查询与演示
    • 使用 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 按总支出降序排列。
  4. 输出结果:展示了每位客户的详细交易统计信息。

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

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

实战目标

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

  1. 每个产品的销售总额。
  2. 每个产品的平均销售价格。
  3. 每个区域的销售总额。
  4. 每个季度的销售总额。
  5. 每个季度的增长率。
  6. 各个产品的销售排名。

数据准备

-- 创建销售数据表CREATETABLEIFNOTEXISTS quarterly_sales ( id INTAUTO_INCREMENTPRIMARYKEY, product_name VARCHAR(255), quantity INT, unit_price DECIMAL(10,2), sale_date DATE, region VARCHAR(50), quarter VARCHAR(10)-- 用于存储季度信息,例如 'Q1_2023');-- 插入示例数据INSERTINTO 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 代码示例

importjava.sql.*;importjava.math.BigDecimal;publicclassSalesAnalysisExample{privatestaticfinalString DB_URL ="jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";privatestaticfinalString DB_USER ="root";// 替换为你的用户名privatestaticfinalString DB_PASSWORD ="password";// 替换为你的密码publicstaticvoidgenerateSalesReport(){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();}}}publicstaticvoidmain(String[] args){generateSalesReport();}}
代码解释
  1. 连接数据库:获取数据库连接。
  2. 产品销售分析
    • 使用 SUM(quantity * unit_price) 计算每个产品的总销售额。
    • 使用 AVG(unit_price) 计算每个产品的平均单价。
    • 使用 SUM(quantity) 计算每个产品的总销量。
    • 使用 GROUP BY product_name 按产品分组。
    • 使用 ORDER BY total_sales DESC 按销售额降序排列。
  3. 区域销售分析
    • 使用 SUM(quantity * unit_price) 计算每个区域的总销售额。
    • 使用 GROUP BY region 按区域分组。
    • 使用 ORDER BY total_sales DESC 按销售额降序排列。
  4. 季度销售分析
    • 使用 SUM(quantity * unit_price) 计算每个季度的总销售额。
    • 使用 GROUP BY quarter 按季度分组。
    • 使用 ORDER BY quarter 按季度升序排列。
  5. 季度增长率分析
    • 使用 WITH 子句创建一个临时结果集 quarterly_totals,计算每个季度的总销售额。
    • 使用 LAG 窗口函数获取上一季度的销售额。
    • 计算增长率:(本期销售额 - 上期销售额) / 上期销售额 * 100
    • 使用 CASE 语句处理第一个季度没有上期数据的情况。
  6. 产品销售排名
    • 使用 ROW_NUMBER() OVER (ORDER BY SUM(quantity * unit_price) DESC) 为产品分配销售额排名。
    • 使用 GROUP BY product_name 按产品分组。
    • 使用 ORDER BY total_sales DESC 按销售额降序排列。
  7. 输出报告:在控制台打印出所有分析结果。

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

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

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

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

🌐 了解更多关于 MySQL 函数的信息,请参考以下官方文档和权威资源:
  1. MySQL 8.0 Reference Manual - Functions and Operators
  2. MySQL 8.0 Reference Manual - String Functions
  3. MySQL 8.0 Reference Manual - Date and Time Functions
  4. MySQL 8.0 Reference Manual - Aggregate (GROUP BY) Functions
  5. W3Schools SQL Functions
  6. GeeksforGeeks - MySQL String Functions
  7. GeeksforGeeks - MySQL Date Functions
  8. GeeksforGeeks - MySQL Aggregate Functions

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

String FunctionsLENGTH/CHAR_LENGTHSUBSTRING/SUBSTRLEFT/RIGHTREPLACEUPPER/LOWERTRIM/LTRIM/RTRIMCONCAT计算字节/字符长度截取子字符串截取左侧/右侧字符串替换大小写转换去除空白字符字符串连接


Mermaid 图表:日期函数关系图 🔄

Date & Time FunctionsNOW/CURDATE/CURTIMEYEAR/MONTH/DAY/HOUR/MINUTE/SECONDDATE_ADD/DATE_SUBDATEDIFF/TIMESTAMPDIFFDATE_FORMAT获取当前时间提取日期组件日期加减计算计算日期差格式化日期输出


Mermaid 图表:聚合函数关系图 🔄

Aggregate FunctionsCOUNTSUMAVGMAX/MINGROUP BY计数求和平均值最大值/最小值分组


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

输出结果数据处理层数据源格式化报表统计图表分析报告字符串函数处理日期函数处理聚合函数处理原始数据表


🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨

Read more

MySQL 慢查询 debug:索引没生效的三重陷阱

MySQL 慢查询 debug:索引没生效的三重陷阱

MySQL 慢查询 debug:索引没生效的三重陷阱 🌟 Hello,我是摘星! 🌈 在彩虹般绚烂的技术栈中,我是那个永不停歇的色彩收集者。 🦋 每一个优化都是我培育的花朵,每一个特性都是我放飞的蝴蝶。 🔬 每一次代码审查都是我的显微镜观察,每一次重构都是我的化学实验。 🎵 在编程的交响乐中,我既是指挥家也是演奏者。让我们一起,在技术的音乐厅里,奏响属于程序员的华美乐章。 目录 MySQL 慢查询 debug:索引没生效的三重陷阱 摘要 1. 慢查询问题的发现与定位 1.1 慢查询日志分析 1.2 性能监控体系搭建 2. 陷阱一:隐式类型转换的索引杀手 2.1 问题现象与案例分析 2.2 类型转换规则与影响 2.3 检测与预防策略 3. 陷阱二:函数包装导致的索引失效 3.1 函数使用的常见误区 3.

By Ne0inhk
[Java EE 进阶] 一文吃透 Spring IoC&DI:核心概念 + 实战用法 + 面试考点

[Java EE 进阶] 一文吃透 Spring IoC&DI:核心概念 + 实战用法 + 面试考点

摘要 :  本文深入解析了Spring框架的核心机制IoC(控制反转)和DI(依赖注入)。通过汽车制造案例,对比传统高耦合开发与Spring解耦方案,阐释IoC将对象创建权交给容器、DI实现依赖注入的核心思想。详细介绍了Bean存储的五种类注解(@Controller/@Service等)和@Bean方法注解,以及三种依赖注入方式(属性/构造方法/Setter注入)。针对多Bean注入问题,提出@Primary、@Qualifier和@Resource三种解决方案。最后总结IoC与DI的关系,指出这是理解Spring框架设计思想的基础,也是学习AOP、事务管理等高级特性的前提。文章系统性地讲解了Spring IoC&DI的核心概念和实战应用 在 Spring 体系的学习中,IoC(控制反转)和 DI(依赖注入)是贯穿始终的核心,也是 Spring 框架的灵魂。掌握 IoC&DI 不仅能理解 Spring 的设计思想,

By Ne0inhk
Hive数据仓库:架构原理与实践指南

Hive数据仓库:架构原理与实践指南

Hive数据仓库:架构原理与实践指南 🌟 你好,我是 励志成为糕手 ! 🌌 在代码的宇宙中,我是那个追逐优雅与性能的星际旅人。 ✨ 每一行代码都是我种下的星光,在逻辑的土壤里生长成璀璨的银河; 🛠️ 每一个算法都是我绘制的星图,指引着数据流动的最短路径; 🔍 每一次调试都是星际对话,用耐心和智慧解开宇宙的谜题。 🚀 准备好开始我们的星际编码之旅了吗? 目录 * Hive数据仓库:架构原理与实践指南 * 摘要 * 一、Hive基础概述 * 1.1 Hive是什么 * 1.2 Hive的发展历程 * 二、Hive架构设计 * 2.1 Hive整体架构 * 2.2 Hive核心组件 * 三、Hive数据类型与表结构 * 3.1 基本数据类型 * 3.2 表的类型 * 四、Hive查询语言HQL * 4.1 数据定义语言(DDL) * 4.2

By Ne0inhk
「youlai-boot」进阶篇:Java & Spring Boot 企业级权限管理系统实战指南(全功能详解)

「youlai-boot」进阶篇:Java & Spring Boot 企业级权限管理系统实战指南(全功能详解)

🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template 🌺 仓库主页: GitCode︱ Gitee ︱ Github 💖 欢迎点赞 👍 收藏 ⭐评论 📝 如有错误敬请纠正! 目录 * 前言 * 项目介绍 * 启动项目 * 克隆代码 * 配置环境 * 启动项目 * 测试启动 * 切换本地环境 * 安装 Docker * 安装中间件 * 修改本地配置 * 配置 MySQL * 配置 Redis * 配置 MinIO (可选) * 配置 Xxl-Job (可选) * 操作指南 * 修改包名 * 代码生成 * 创建数据表 * 启动项目 * 生成代码 * 集成代码 * 功能测试 * 接口文档 * 登录接口 * 设置全局 Token

By Ne0inhk