commit 4c7451db2502a6f12e5e46169cc23e3adc8e4c73 Author: HP Date: Wed Jun 25 19:21:02 2025 +0800 Initial commit diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..7b41856 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "mcp__zen__chat" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e69de29 diff --git a/Java语法完整指南.md b/Java语法完整指南.md new file mode 100644 index 0000000..8a1bd0f --- /dev/null +++ b/Java语法完整指南.md @@ -0,0 +1,2807 @@ +# Java 语法完整指南 + +## 目录 +1. [Java基础概念](#java基础概念) +2. [数据类型](#数据类型) +3. [变量与常量](#变量与常量) +4. [运算符](#运算符) +5. [控制结构](#控制结构) +6. [数组](#数组) +7. [方法](#方法) +8. [面向对象编程](#面向对象编程) +9. [接口与抽象类](#接口与抽象类) +10. [异常处理](#异常处理) +11. [集合框架](#集合框架) +12. [字符串处理](#字符串处理) +13. [文件IO](#文件io) +14. [多线程](#多线程) +15. [泛型](#泛型) +16. [注解](#注解) +17. [Lambda表达式](#lambda表达式) +18. [Stream API](#stream-api) + +--- + +## Java基础概念 + +### 1.1 Java程序结构 +```java +// 包声明(可选) +package com.example.demo; + +// 导入语句(可选) +import java.util.Scanner; +import java.util.*; + +// 公共类定义 +public class HelloWorld { + // 主方法 - 程序入口点 + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} +``` + +### 1.2 编译与运行 +```bash +# 编译Java源文件 +javac HelloWorld.java + +# 运行Java程序 +java HelloWorld +``` + +### 1.3 Java特性 +- **面向对象**:封装、继承、多态 +- **平台无关性**:一次编写,到处运行 +- **自动内存管理**:垃圾回收机制 +- **强类型检查**:编译时类型检查 +- **多线程支持**:内置多线程支持 + +--- + +## 数据类型 + +### 2.1 基本数据类型 +```java +// 整型 +byte b = 127; // 8位,-128 到 127 +short s = 32767; // 16位,-32,768 到 32,767 +int i = 2147483647; // 32位,-2^31 到 2^31-1 +long l = 9223372036854775807L; // 64位,需要L后缀 + +// 浮点型 +float f = 3.14f; // 32位,需要f后缀 +double d = 3.14159; // 64位,默认浮点类型 + +// 字符型 +char c = 'A'; // 16位Unicode字符 +char unicode = '\u0041'; // Unicode表示法 + +// 布尔型 +boolean flag = true; // true或false +``` + +### 2.2 引用数据类型 +```java +// 字符串 +String str = "Hello Java"; +String str2 = new String("Hello Java"); + +// 数组 +int[] numbers = {1, 2, 3, 4, 5}; +String[] names = new String[10]; + +// 对象 +Scanner scanner = new Scanner(System.in); +``` + +### 2.3 类型转换 +```java +// 自动类型转换(小范围到大范围) +int i = 100; +long l = i; // int自动转为long +double d = l; // long自动转为double + +// 强制类型转换 +double d2 = 3.14; +int i2 = (int) d2; // 结果为3,小数部分丢失 + +// 字符串转换 +String str = "123"; +int num = Integer.parseInt(str); // 字符串转int +double dbl = Double.parseDouble("3.14"); // 字符串转double +String s = String.valueOf(num); // 数字转字符串 +``` + +--- + +## 变量与常量 + +### 3.1 变量声明 +```java +// 变量声明语法:数据类型 变量名 = 初始值; +int age = 25; +String name = "张三"; +boolean isStudent = true; + +// 多变量声明 +int a, b, c; +int x = 1, y = 2, z = 3; + +// 变量作用域 +public class VariableScope { + static int globalVar = 100; // 类变量 + int instanceVar = 200; // 实例变量 + + public void method() { + int localVar = 300; // 局部变量 + // localVar只在方法内有效 + } +} +``` + +### 3.2 常量定义 +```java +// 使用final关键字定义常量 +final int MAX_SIZE = 100; +final double PI = 3.14159; +final String COMPANY_NAME = "科技有限公司"; + +// 类常量(静态常量) +public static final int DEFAULT_CAPACITY = 10; +public static final String VERSION = "1.0.0"; +``` + +### 3.3 变量命名规范 +```java +// 良好的命名示例 +int studentAge; // 驼峰命名法 +String firstName; // 驼峰命名法 +boolean isValid; // 布尔值用is开头 +final int MAX_COUNT = 50; // 常量用大写+下划线 + +// 避免的命名 +int a, b, c; // 无意义的名称 +String s1, s2; // 无意义的名称 +``` + +--- + +## 运算符 + +### 4.1 算术运算符 +```java +int a = 10, b = 3; + +int sum = a + b; // 加法:13 +int diff = a - b; // 减法:7 +int product = a * b; // 乘法:30 +int quotient = a / b; // 整除:3 +int remainder = a % b; // 取余:1 + +// 自增自减 +int x = 5; +x++; // 后自增,x变为6 +++x; // 前自增,x变为7 +x--; // 后自减,x变为6 +--x; // 前自减,x变为5 + +int y = 10; +int result1 = y++; // result1 = 10, y = 11 +int result2 = ++y; // result2 = 12, y = 12 +``` + +### 4.2 关系运算符 +```java +int a = 10, b = 5; + +boolean eq = (a == b); // 等于:false +boolean ne = (a != b); // 不等于:true +boolean gt = (a > b); // 大于:true +boolean ge = (a >= b); // 大于等于:true +boolean lt = (a < b); // 小于:false +boolean le = (a <= b); // 小于等于:false + +// 字符串比较 +String str1 = "Hello"; +String str2 = "Hello"; +boolean same = str1.equals(str2); // 内容比较:true +boolean ref = (str1 == str2); // 引用比较:可能为false +``` + +### 4.3 逻辑运算符 +```java +boolean a = true, b = false; + +boolean and = a && b; // 逻辑与:false +boolean or = a || b; // 逻辑或:true +boolean not = !a; // 逻辑非:false + +// 短路运算 +boolean result = (a || (10/0 > 0)); // 不会抛异常,因为a为true +``` + +### 4.4 位运算符 +```java +int a = 60; // 二进制:111100 +int b = 13; // 二进制:001101 + +int and = a & b; // 按位与:12 (001100) +int or = a | b; // 按位或:61 (111101) +int xor = a ^ b; // 按位异或:49 (110001) +int not = ~a; // 按位取反:-61 +int left = a << 2; // 左移:240 (11110000) +int right = a >> 2;// 右移:15 (1111) +``` + +### 4.5 赋值运算符 +```java +int x = 10; + +x += 5; // 等价于 x = x + 5,结果:15 +x -= 3; // 等价于 x = x - 3,结果:12 +x *= 2; // 等价于 x = x * 2,结果:24 +x /= 4; // 等价于 x = x / 4,结果:6 +x %= 4; // 等价于 x = x % 4,结果:2 +``` + +### 4.6 三元运算符 +```java +int a = 10, b = 5; +int max = (a > b) ? a : b; // 如果a>b则取a,否则取b + +String result = (score >= 60) ? "及格" : "不及格"; +``` + +--- + +## 控制结构 + +### 5.1 条件语句 + +#### if语句 +```java +int score = 85; + +// 单分支 +if (score >= 60) { + System.out.println("及格"); +} + +// 双分支 +if (score >= 60) { + System.out.println("及格"); +} else { + System.out.println("不及格"); +} + +// 多分支 +if (score >= 90) { + System.out.println("优秀"); +} else if (score >= 80) { + System.out.println("良好"); +} else if (score >= 60) { + System.out.println("及格"); +} else { + System.out.println("不及格"); +} +``` + +#### switch语句 +```java +int dayOfWeek = 3; + +switch (dayOfWeek) { + case 1: + System.out.println("星期一"); + break; + case 2: + System.out.println("星期二"); + break; + case 3: + System.out.println("星期三"); + break; + default: + System.out.println("其他"); + break; +} + +// Java 14+ 新语法 +String result = switch (dayOfWeek) { + case 1, 2, 3, 4, 5 -> "工作日"; + case 6, 7 -> "周末"; + default -> "无效"; +}; +``` + +### 5.2 循环语句 + +#### for循环 +```java +// 基本for循环 +for (int i = 0; i < 10; i++) { + System.out.println("第" + i + "次循环"); +} + +// 增强for循环(for-each) +int[] numbers = {1, 2, 3, 4, 5}; +for (int num : numbers) { + System.out.println(num); +} + +// 嵌套循环 +for (int i = 1; i <= 3; i++) { + for (int j = 1; j <= 3; j++) { + System.out.print(i + "," + j + " "); + } + System.out.println(); +} +``` + +#### while循环 +```java +int i = 0; +while (i < 5) { + System.out.println("i = " + i); + i++; +} + +// 无限循环 +while (true) { + // 某些条件下break + if (condition) { + break; + } +} +``` + +#### do-while循环 +```java +int i = 0; +do { + System.out.println("i = " + i); + i++; +} while (i < 5); +``` + +### 5.3 跳转语句 +```java +// break:跳出循环 +for (int i = 0; i < 10; i++) { + if (i == 5) { + break; // 跳出循环 + } + System.out.println(i); +} + +// continue:跳过本次循环 +for (int i = 0; i < 10; i++) { + if (i % 2 == 0) { + continue; // 跳过偶数 + } + System.out.println(i); +} + +// 标签跳转 +outer: for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (i == 1 && j == 1) { + break outer; // 跳出外层循环 + } + System.out.println(i + "," + j); + } +} +``` + +--- + +## 数组 + +### 6.1 一维数组 +```java +// 数组声明和初始化 +int[] numbers = new int[5]; // 声明长度为5的整型数组 +int[] values = {1, 2, 3, 4, 5}; // 直接初始化 +int[] scores = new int[]{90, 85, 92}; // 另一种初始化方式 + +// 数组访问 +numbers[0] = 10; // 设置第一个元素 +int first = numbers[0]; // 获取第一个元素 +int length = numbers.length; // 获取数组长度 + +// 遍历数组 +for (int i = 0; i < numbers.length; i++) { + System.out.println(numbers[i]); +} + +// 增强for循环遍历 +for (int num : numbers) { + System.out.println(num); +} +``` + +### 6.2 二维数组 +```java +// 二维数组声明和初始化 +int[][] matrix = new int[3][4]; // 3行4列的矩阵 +int[][] grid = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} +}; + +// 访问二维数组 +matrix[0][0] = 1; // 设置第一行第一列 +int value = grid[1][2]; // 获取第二行第三列的值 + +// 遍历二维数组 +for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + System.out.print(grid[i][j] + " "); + } + System.out.println(); +} + +// 增强for循环遍历二维数组 +for (int[] row : grid) { + for (int value : row) { + System.out.print(value + " "); + } + System.out.println(); +} +``` + +### 6.3 数组常用操作 +```java +import java.util.Arrays; + +int[] numbers = {5, 2, 8, 1, 9}; + +// 数组排序 +Arrays.sort(numbers); +System.out.println(Arrays.toString(numbers)); // [1, 2, 5, 8, 9] + +// 数组查找 +int index = Arrays.binarySearch(numbers, 5); // 返回索引 + +// 数组填充 +int[] filled = new int[5]; +Arrays.fill(filled, 10); // 所有元素填充为10 + +// 数组复制 +int[] copy1 = Arrays.copyOf(numbers, numbers.length); +int[] copy2 = numbers.clone(); + +// 数组比较 +boolean equal = Arrays.equals(numbers, copy1); +``` + +--- + +## 方法 + +### 7.1 方法定义和调用 +```java +public class Calculator { + // 基本方法定义 + public static int add(int a, int b) { + return a + b; + } + + // 无返回值方法 + public static void printMessage(String message) { + System.out.println(message); + } + + // 实例方法 + public int multiply(int a, int b) { + return a * b; + } + + public static void main(String[] args) { + // 调用静态方法 + int sum = add(5, 3); + printMessage("Hello"); + + // 调用实例方法 + Calculator calc = new Calculator(); + int product = calc.multiply(4, 6); + } +} +``` + +### 7.2 方法重载 +```java +public class MathUtils { + // 方法重载:相同方法名,不同参数 + public static int add(int a, int b) { + return a + b; + } + + public static double add(double a, double b) { + return a + b; + } + + public static int add(int a, int b, int c) { + return a + b + c; + } + + public static String add(String a, String b) { + return a + b; + } +} +``` + +### 7.3 可变参数 +```java +public class VarArgs { + // 可变参数方法 + public static int sum(int... numbers) { + int total = 0; + for (int num : numbers) { + total += num; + } + return total; + } + + public static void main(String[] args) { + System.out.println(sum(1, 2, 3)); // 6 + System.out.println(sum(1, 2, 3, 4, 5)); // 15 + System.out.println(sum()); // 0 + } +} +``` + +### 7.4 递归方法 +```java +public class Recursion { + // 计算阶乘 + public static long factorial(int n) { + if (n <= 1) { + return 1; // 基本情况 + } else { + return n * factorial(n - 1); // 递归调用 + } + } + + // 计算斐波那契数列 + public static int fibonacci(int n) { + if (n <= 1) { + return n; + } else { + return fibonacci(n - 1) + fibonacci(n - 2); + } + } +} +``` + +--- + +## 面向对象编程 + +### 8.1 类和对象 +```java +// 类定义 +public class Student { + // 成员变量(属性) + private String name; + private int age; + private String studentId; + + // 构造方法 + public Student() { + // 无参构造方法 + } + + public Student(String name, int age, String studentId) { + this.name = name; + this.age = age; + this.studentId = studentId; + } + + // getter和setter方法 + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + if (age >= 0 && age <= 150) { + this.age = age; + } + } + + // 成员方法 + public void study(String subject) { + System.out.println(name + "正在学习" + subject); + } + + public void displayInfo() { + System.out.println("姓名:" + name + ",年龄:" + age + ",学号:" + studentId); + } + + // toString方法重写 + @Override + public String toString() { + return "Student{name='" + name + "', age=" + age + ", studentId='" + studentId + "'}"; + } +} + +// 使用类创建对象 +public class TestStudent { + public static void main(String[] args) { + // 创建对象 + Student student1 = new Student(); + student1.setName("张三"); + student1.setAge(20); + + Student student2 = new Student("李四", 19, "2023001"); + + // 调用方法 + student1.study("Java"); + student2.displayInfo(); + } +} +``` + +### 8.2 封装 +```java +public class BankAccount { + // 私有成员变量,外部无法直接访问 + private String accountNumber; + private double balance; + private String ownerName; + + public BankAccount(String accountNumber, String ownerName, double initialBalance) { + this.accountNumber = accountNumber; + this.ownerName = ownerName; + this.balance = initialBalance >= 0 ? initialBalance : 0; + } + + // 提供公共方法来访问和操作私有数据 + public void deposit(double amount) { + if (amount > 0) { + balance += amount; + System.out.println("存款成功,当前余额:" + balance); + } else { + System.out.println("存款金额必须大于0"); + } + } + + public boolean withdraw(double amount) { + if (amount > 0 && amount <= balance) { + balance -= amount; + System.out.println("取款成功,当前余额:" + balance); + return true; + } else { + System.out.println("取款失败:余额不足或金额无效"); + return false; + } + } + + // 只读属性 + public double getBalance() { + return balance; + } + + public String getAccountNumber() { + return accountNumber; + } + + public String getOwnerName() { + return ownerName; + } +} +``` + +### 8.3 继承 +```java +// 父类(基类) +public class Animal { + protected String name; + protected int age; + + public Animal(String name, int age) { + this.name = name; + this.age = age; + } + + public void eat() { + System.out.println(name + "正在吃东西"); + } + + public void sleep() { + System.out.println(name + "正在睡觉"); + } + + public void makeSound() { + System.out.println(name + "发出声音"); + } +} + +// 子类(派生类) +public class Dog extends Animal { + private String breed; + + public Dog(String name, int age, String breed) { + super(name, age); // 调用父类构造方法 + this.breed = breed; + } + + // 重写父类方法 + @Override + public void makeSound() { + System.out.println(name + "汪汪叫"); + } + + // 子类特有方法 + public void wagTail() { + System.out.println(name + "正在摇尾巴"); + } + + public void fetch() { + System.out.println(name + "正在捡球"); + } +} + +public class Cat extends Animal { + public Cat(String name, int age) { + super(name, age); + } + + @Override + public void makeSound() { + System.out.println(name + "喵喵叫"); + } + + public void climb() { + System.out.println(name + "正在爬树"); + } +} +``` + +### 8.4 多态 +```java +public class PolymorphismDemo { + public static void main(String[] args) { + // 多态:父类引用指向子类对象 + Animal[] animals = { + new Dog("旺财", 3, "金毛"), + new Cat("咪咪", 2), + new Dog("大黄", 5, "土狗") + }; + + // 多态调用:同一个方法调用,不同的实现 + for (Animal animal : animals) { + animal.eat(); // 调用父类方法 + animal.makeSound(); // 调用各自重写的方法 + System.out.println("---"); + } + + // 类型检查和转换 + for (Animal animal : animals) { + if (animal instanceof Dog) { + Dog dog = (Dog) animal; // 向下转型 + dog.wagTail(); // 调用子类特有方法 + } else if (animal instanceof Cat) { + Cat cat = (Cat) animal; + cat.climb(); + } + } + } +} +``` + +--- + +## 接口与抽象类 + +### 9.1 接口 +```java +// 接口定义 +public interface Drawable { + // 接口中的变量默认是public static final + double PI = 3.14159; + + // 抽象方法(默认public abstract) + void draw(); + void resize(double scale); + + // Java 8+:默认方法 + default void display() { + System.out.println("显示图形"); + } + + // Java 8+:静态方法 + static void printInfo() { + System.out.println("这是一个可绘制接口"); + } +} + +// 接口实现 +public class Circle implements Drawable { + private double radius; + + public Circle(double radius) { + this.radius = radius; + } + + @Override + public void draw() { + System.out.println("绘制半径为" + radius + "的圆形"); + } + + @Override + public void resize(double scale) { + radius *= scale; + System.out.println("圆形缩放后半径为:" + radius); + } +} + +// 多接口实现 +public interface Movable { + void move(int x, int y); +} + +public class Rectangle implements Drawable, Movable { + private double width, height; + private int x, y; + + public Rectangle(double width, double height) { + this.width = width; + this.height = height; + } + + @Override + public void draw() { + System.out.println("绘制矩形:宽=" + width + ",高=" + height); + } + + @Override + public void resize(double scale) { + width *= scale; + height *= scale; + } + + @Override + public void move(int x, int y) { + this.x = x; + this.y = y; + System.out.println("矩形移动到:(" + x + ", " + y + ")"); + } +} +``` + +### 9.2 抽象类 +```java +// 抽象类定义 +public abstract class Shape { + protected String color; + protected double x, y; + + public Shape(String color, double x, double y) { + this.color = color; + this.x = x; + this.y = y; + } + + // 抽象方法:子类必须实现 + public abstract double getArea(); + public abstract double getPerimeter(); + + // 具体方法:子类可以直接使用 + public void setPosition(double x, double y) { + this.x = x; + this.y = y; + } + + public void setColor(String color) { + this.color = color; + } + + public void displayInfo() { + System.out.println("颜色:" + color + ",位置:(" + x + ", " + y + ")"); + System.out.println("面积:" + getArea() + ",周长:" + getPerimeter()); + } +} + +// 抽象类的实现 +public class Triangle extends Shape { + private double side1, side2, side3; + + public Triangle(String color, double x, double y, double side1, double side2, double side3) { + super(color, x, y); + this.side1 = side1; + this.side2 = side2; + this.side3 = side3; + } + + @Override + public double getArea() { + // 海伦公式计算三角形面积 + double s = (side1 + side2 + side3) / 2; + return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3)); + } + + @Override + public double getPerimeter() { + return side1 + side2 + side3; + } +} +``` + +--- + +## 异常处理 + +### 10.1 异常类型 +```java +// Java异常层次结构 +/* +Throwable +├── Error(错误) +│ ├── OutOfMemoryError +│ ├── StackOverflowError +│ └── ... +└── Exception(异常) + ├── RuntimeException(运行时异常,非检查异常) + │ ├── NullPointerException + │ ├── ArrayIndexOutOfBoundsException + │ ├── IllegalArgumentException + │ └── ... + └── 检查异常 + ├── IOException + ├── ClassNotFoundException + └── ... +*/ +``` + +### 10.2 异常处理语法 +```java +public class ExceptionHandling { + public static void main(String[] args) { + // try-catch基本语法 + try { + int result = 10 / 0; // 可能抛出异常的代码 + } catch (ArithmeticException e) { + System.out.println("算术异常:" + e.getMessage()); + } + + // 多个catch块 + try { + int[] arr = new int[5]; + arr[10] = 100; // 数组越界 + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("数组越界:" + e.getMessage()); + } catch (Exception e) { + System.out.println("其他异常:" + e.getMessage()); + } + + // try-catch-finally + try { + // 可能出现异常的代码 + performOperation(); + } catch (Exception e) { + System.out.println("捕获异常:" + e.getMessage()); + } finally { + // 无论是否出现异常都会执行 + System.out.println("清理资源"); + } + + // try-with-resources(Java 7+) + try (Scanner scanner = new Scanner(System.in)) { + String input = scanner.nextLine(); + } catch (Exception e) { + e.printStackTrace(); + } + // scanner会自动关闭 + } + + public static void performOperation() throws Exception { + // 可能抛出异常的方法 + throw new Exception("操作失败"); + } +} +``` + +### 10.3 自定义异常 +```java +// 自定义异常类 +public class InsufficientBalanceException extends Exception { + private double balance; + private double amount; + + public InsufficientBalanceException(double balance, double amount) { + super("余额不足:当前余额" + balance + ",尝试取款" + amount); + this.balance = balance; + this.amount = amount; + } + + public double getBalance() { + return balance; + } + + public double getAmount() { + return amount; + } +} + +// 使用自定义异常 +public class BankAccountWithException { + private double balance; + + public BankAccountWithException(double initialBalance) { + this.balance = initialBalance; + } + + public void withdraw(double amount) throws InsufficientBalanceException { + if (amount > balance) { + throw new InsufficientBalanceException(balance, amount); + } + balance -= amount; + System.out.println("取款成功,余额:" + balance); + } + + public static void main(String[] args) { + BankAccountWithException account = new BankAccountWithException(1000); + + try { + account.withdraw(1500); // 会抛出异常 + } catch (InsufficientBalanceException e) { + System.out.println("取款失败:" + e.getMessage()); + System.out.println("当前余额:" + e.getBalance()); + } + } +} +``` + +### 10.4 异常传播和处理最佳实践 +```java +public class ExceptionBestPractices { + // 方法声明抛出异常 + public void readFile(String filename) throws IOException { + FileReader file = new FileReader(filename); + // 文件操作... + file.close(); + } + + // 异常链 + public void processData() throws DataProcessingException { + try { + // 一些可能出错的操作 + riskyOperation(); + } catch (SQLException e) { + // 包装异常,保留原始异常信息 + throw new DataProcessingException("数据处理失败", e); + } + } + + // 资源管理的最佳实践 + public String readFileContent(String filename) { + StringBuilder content = new StringBuilder(); + + try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { + String line; + while ((line = reader.readLine()) != null) { + content.append(line).append("\n"); + } + } catch (IOException e) { + System.err.println("读取文件失败:" + e.getMessage()); + e.printStackTrace(); + } + + return content.toString(); + } +} +``` + +--- + +## 集合框架 + +### 11.1 List接口 +```java +import java.util.*; + +public class ListDemo { + public static void main(String[] args) { + // ArrayList:动态数组 + List arrayList = new ArrayList<>(); + arrayList.add("apple"); + arrayList.add("banana"); + arrayList.add("orange"); + arrayList.add(1, "grape"); // 在指定位置插入 + + // LinkedList:双向链表 + List linkedList = new LinkedList<>(); + linkedList.add("first"); + linkedList.add("second"); + ((LinkedList) linkedList).addFirst("zero"); // 在开头添加 + ((LinkedList) linkedList).addLast("last"); // 在末尾添加 + + // Vector:线程安全的动态数组 + List vector = new Vector<>(); + vector.add(1); + vector.add(2); + vector.add(3); + + // 遍历List + for (String item : arrayList) { + System.out.println(item); + } + + // 使用迭代器 + Iterator iterator = arrayList.iterator(); + while (iterator.hasNext()) { + System.out.println(iterator.next()); + } + + // 常用操作 + System.out.println("大小:" + arrayList.size()); + System.out.println("包含apple:" + arrayList.contains("apple")); + System.out.println("apple的索引:" + arrayList.indexOf("apple")); + arrayList.remove("banana"); // 删除元素 + arrayList.remove(0); // 删除指定位置元素 + } +} +``` + +### 11.2 Set接口 +```java +import java.util.*; + +public class SetDemo { + public static void main(String[] args) { + // HashSet:基于哈希表,无序,不允许重复 + Set hashSet = new HashSet<>(); + hashSet.add("java"); + hashSet.add("python"); + hashSet.add("javascript"); + hashSet.add("java"); // 重复元素不会被添加 + + System.out.println("HashSet: " + hashSet); + + // LinkedHashSet:保持插入顺序的HashSet + Set linkedHashSet = new LinkedHashSet<>(); + linkedHashSet.add("first"); + linkedHashSet.add("second"); + linkedHashSet.add("third"); + + System.out.println("LinkedHashSet: " + linkedHashSet); + + // TreeSet:基于红黑树,自动排序 + Set treeSet = new TreeSet<>(); + treeSet.add(5); + treeSet.add(2); + treeSet.add(8); + treeSet.add(1); + + System.out.println("TreeSet: " + treeSet); // 自动排序输出 + + // 集合操作 + Set set1 = new HashSet<>(Arrays.asList("a", "b", "c")); + Set set2 = new HashSet<>(Arrays.asList("b", "c", "d")); + + // 并集 + Set union = new HashSet<>(set1); + union.addAll(set2); + System.out.println("并集: " + union); + + // 交集 + Set intersection = new HashSet<>(set1); + intersection.retainAll(set2); + System.out.println("交集: " + intersection); + + // 差集 + Set difference = new HashSet<>(set1); + difference.removeAll(set2); + System.out.println("差集: " + difference); + } +} +``` + +### 11.3 Map接口 +```java +import java.util.*; + +public class MapDemo { + public static void main(String[] args) { + // HashMap:基于哈希表,无序 + Map hashMap = new HashMap<>(); + hashMap.put("apple", 5); + hashMap.put("banana", 3); + hashMap.put("orange", 8); + + // LinkedHashMap:保持插入顺序 + Map linkedHashMap = new LinkedHashMap<>(); + linkedHashMap.put("first", 1); + linkedHashMap.put("second", 2); + linkedHashMap.put("third", 3); + + // TreeMap:基于红黑树,按键自动排序 + Map treeMap = new TreeMap<>(); + treeMap.put("zebra", 1); + treeMap.put("apple", 2); + treeMap.put("banana", 3); + + System.out.println("TreeMap (自动排序): " + treeMap); + + // Map操作 + System.out.println("apple的值: " + hashMap.get("apple")); + System.out.println("包含键apple: " + hashMap.containsKey("apple")); + System.out.println("包含值5: " + hashMap.containsValue(5)); + + // 遍历Map + // 方法1:遍历键 + for (String key : hashMap.keySet()) { + System.out.println(key + " = " + hashMap.get(key)); + } + + // 方法2:遍历键值对 + for (Map.Entry entry : hashMap.entrySet()) { + System.out.println(entry.getKey() + " = " + entry.getValue()); + } + + // 方法3:Java 8 forEach + hashMap.forEach((key, value) -> System.out.println(key + " = " + value)); + + // Map的其他操作 + hashMap.putIfAbsent("grape", 6); // 键不存在时才添加 + hashMap.replace("apple", 10); // 替换值 + hashMap.remove("banana"); // 删除键值对 + } +} +``` + +### 11.4 Collections工具类 +```java +import java.util.*; + +public class CollectionsDemo { + public static void main(String[] args) { + List numbers = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3)); + + // 排序 + Collections.sort(numbers); + System.out.println("升序排序: " + numbers); + + Collections.sort(numbers, Collections.reverseOrder()); + System.out.println("降序排序: " + numbers); + + // 自定义比较器排序 + List words = Arrays.asList("apple", "pie", "a", "application"); + Collections.sort(words, Comparator.comparing(String::length)); + System.out.println("按长度排序: " + words); + + // 查找 + Collections.sort(numbers); // 二分查找需要有序数组 + int index = Collections.binarySearch(numbers, 5); + System.out.println("5的位置: " + index); + + // 其他操作 + Collections.shuffle(numbers); // 随机打乱 + Collections.reverse(numbers); // 反转 + Collections.fill(numbers, 0); // 填充 + System.out.println("最大值: " + Collections.max(numbers)); + System.out.println("最小值: " + Collections.min(numbers)); + + // 创建不可变集合 + List immutableList = Collections.unmodifiableList( + Arrays.asList("a", "b", "c")); + + // 线程安全集合 + List syncList = Collections.synchronizedList(new ArrayList<>()); + Map syncMap = Collections.synchronizedMap(new HashMap<>()); + } +} +``` + +--- + +## 字符串处理 + +### 12.1 String类 +```java +public class StringDemo { + public static void main(String[] args) { + // 字符串创建 + String str1 = "Hello"; // 字符串字面量 + String str2 = new String("Hello"); // 使用构造方法 + String str3 = "World"; + + // 字符串连接 + String result1 = str1 + " " + str3; + String result2 = str1.concat(" ").concat(str3); + + // 字符串长度 + System.out.println("长度: " + str1.length()); + + // 字符串比较 + System.out.println("相等: " + str1.equals("Hello")); + System.out.println("忽略大小写相等: " + str1.equalsIgnoreCase("hello")); + System.out.println("比较: " + str1.compareTo("Help")); // 返回int + + // 字符串查找 + String text = "Hello Java World"; + System.out.println("包含Java: " + text.contains("Java")); + System.out.println("以Hello开头: " + text.startsWith("Hello")); + System.out.println("以World结尾: " + text.endsWith("World")); + System.out.println("Java的位置: " + text.indexOf("Java")); + System.out.println("最后一个o的位置: " + text.lastIndexOf("o")); + + // 字符串截取 + System.out.println("子字符串: " + text.substring(6)); // 从位置6开始 + System.out.println("子字符串: " + text.substring(6, 10)); // 从6到10(不包含10) + + // 字符串替换 + System.out.println("替换: " + text.replace("Java", "Python")); + System.out.println("替换第一个: " + text.replaceFirst("o", "0")); + System.out.println("正则替换: " + text.replaceAll("\\s+", "_")); + + // 字符串分割 + String csv = "apple,banana,orange,grape"; + String[] fruits = csv.split(","); + for (String fruit : fruits) { + System.out.println(fruit); + } + + // 大小写转换 + System.out.println("大写: " + text.toUpperCase()); + System.out.println("小写: " + text.toLowerCase()); + + // 去除空白 + String spaced = " Hello World "; + System.out.println("去除前后空白: '" + spaced.trim() + "'"); + + // 字符串格式化 + String formatted = String.format("姓名: %s, 年龄: %d, 分数: %.2f", + "张三", 25, 89.567); + System.out.println(formatted); + } +} +``` + +### 12.2 StringBuilder和StringBuffer +```java +public class StringBuilderDemo { + public static void main(String[] args) { + // StringBuilder:非线程安全,性能更好 + StringBuilder sb = new StringBuilder(); + sb.append("Hello"); + sb.append(" "); + sb.append("World"); + sb.insert(5, " Java"); // 在位置5插入 + sb.delete(5, 10); // 删除位置5到10的字符 + sb.reverse(); // 反转字符串 + + System.out.println("StringBuilder结果: " + sb.toString()); + + // StringBuffer:线程安全 + StringBuffer sbf = new StringBuffer("Hello"); + sbf.append(" World"); + System.out.println("StringBuffer结果: " + sbf.toString()); + + // 性能比较示例 + long startTime, endTime; + + // String连接(效率低) + startTime = System.currentTimeMillis(); + String str = ""; + for (int i = 0; i < 10000; i++) { + str += "a"; + } + endTime = System.currentTimeMillis(); + System.out.println("String连接耗时: " + (endTime - startTime) + "ms"); + + // StringBuilder连接(效率高) + startTime = System.currentTimeMillis(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 10000; i++) { + builder.append("a"); + } + String result = builder.toString(); + endTime = System.currentTimeMillis(); + System.out.println("StringBuilder连接耗时: " + (endTime - startTime) + "ms"); + } +} +``` + +### 12.3 正则表达式 +```java +import java.util.regex.*; + +public class RegexDemo { + public static void main(String[] args) { + String text = "联系电话:138-0013-8000,邮箱:user@example.com"; + + // 匹配手机号 + String phonePattern = "\\d{3}-\\d{4}-\\d{4}"; + Pattern phoneRegex = Pattern.compile(phonePattern); + Matcher phoneMatcher = phoneRegex.matcher(text); + + if (phoneMatcher.find()) { + System.out.println("找到手机号: " + phoneMatcher.group()); + } + + // 匹配邮箱 + String emailPattern = "[\\w._%+-]+@[\\w.-]+\\.[A-Za-z]{2,}"; + Pattern emailRegex = Pattern.compile(emailPattern); + Matcher emailMatcher = emailRegex.matcher(text); + + if (emailMatcher.find()) { + System.out.println("找到邮箱: " + emailMatcher.group()); + } + + // 使用String类的正则方法 + String input = "abc123def456ghi"; + + // 检查是否匹配 + System.out.println("包含数字: " + input.matches(".*\\d+.*")); + + // 分割 + String[] parts = input.split("\\d+"); + System.out.println("分割结果: " + Arrays.toString(parts)); + + // 替换 + String replaced = input.replaceAll("\\d+", "#"); + System.out.println("替换数字: " + replaced); + + // 常用正则表达式 + validateInputs(); + } + + public static void validateInputs() { + String[] testData = { + "user@example.com", // 邮箱 + "13800138000", // 手机号 + "abc123", // 用户名 + "P@ssw0rd123" // 密码 + }; + + // 邮箱验证 + String emailRegex = "^[\\w._%+-]+@[\\w.-]+\\.[A-Za-z]{2,}$"; + + // 手机号验证(中国大陆) + String phoneRegex = "^1[3-9]\\d{9}$"; + + // 用户名验证(字母开头,包含字母数字下划线,3-16位) + String usernameRegex = "^[a-zA-Z][a-zA-Z0-9_]{2,15}$"; + + // 密码验证(至少8位,包含大小写字母、数字、特殊字符) + String passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; + + System.out.println("邮箱验证: " + testData[0].matches(emailRegex)); + System.out.println("手机号验证: " + testData[1].matches(phoneRegex)); + System.out.println("用户名验证: " + testData[2].matches(usernameRegex)); + System.out.println("密码验证: " + testData[3].matches(passwordRegex)); + } +} +``` + +--- + +## 文件IO + +### 13.1 文件操作基础 +```java +import java.io.*; +import java.nio.file.*; +import java.util.List; + +public class FileIODemo { + public static void main(String[] args) { + // File类基本操作 + File file = new File("test.txt"); + File directory = new File("testDir"); + + try { + // 文件操作 + if (!file.exists()) { + file.createNewFile(); + System.out.println("文件创建成功"); + } + + System.out.println("文件名: " + file.getName()); + System.out.println("绝对路径: " + file.getAbsolutePath()); + System.out.println("文件大小: " + file.length() + " 字节"); + System.out.println("是否为文件: " + file.isFile()); + System.out.println("是否为目录: " + file.isDirectory()); + System.out.println("可读: " + file.canRead()); + System.out.println("可写: " + file.canWrite()); + System.out.println("最后修改时间: " + new Date(file.lastModified())); + + // 目录操作 + if (!directory.exists()) { + directory.mkdir(); // 创建单级目录 + // directory.mkdirs(); // 创建多级目录 + System.out.println("目录创建成功"); + } + + // 列出目录内容 + File currentDir = new File("."); + String[] files = currentDir.list(); + if (files != null) { + System.out.println("当前目录文件:"); + for (String fileName : files) { + System.out.println(" " + fileName); + } + } + + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +### 13.2 字符流操作 +```java +import java.io.*; + +public class CharacterStreamDemo { + public static void main(String[] args) { + writeTextFile(); + readTextFile(); + copyTextFile(); + } + + // 写入文本文件 + public static void writeTextFile() { + try (FileWriter writer = new FileWriter("output.txt"); + BufferedWriter bufferedWriter = new BufferedWriter(writer)) { + + bufferedWriter.write("第一行文本\n"); + bufferedWriter.write("第二行文本\n"); + bufferedWriter.write("第三行文本\n"); + bufferedWriter.flush(); // 强制写入 + + System.out.println("文件写入完成"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 读取文本文件 + public static void readTextFile() { + try (FileReader reader = new FileReader("output.txt"); + BufferedReader bufferedReader = new BufferedReader(reader)) { + + String line; + int lineNumber = 1; + + while ((line = bufferedReader.readLine()) != null) { + System.out.println("行" + lineNumber + ": " + line); + lineNumber++; + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 复制文本文件 + public static void copyTextFile() { + try (FileReader reader = new FileReader("output.txt"); + FileWriter writer = new FileWriter("copy.txt"); + BufferedReader bufferedReader = new BufferedReader(reader); + BufferedWriter bufferedWriter = new BufferedWriter(writer)) { + + String line; + while ((line = bufferedReader.readLine()) != null) { + bufferedWriter.write(line); + bufferedWriter.newLine(); + } + + System.out.println("文件复制完成"); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +### 13.3 字节流操作 +```java +import java.io.*; + +public class ByteStreamDemo { + public static void main(String[] args) { + writeBinaryFile(); + readBinaryFile(); + copyBinaryFile(); + } + + // 写入二进制文件 + public static void writeBinaryFile() { + try (FileOutputStream fos = new FileOutputStream("data.bin"); + BufferedOutputStream bos = new BufferedOutputStream(fos); + DataOutputStream dos = new DataOutputStream(bos)) { + + // 写入不同类型的数据 + dos.writeInt(42); + dos.writeDouble(3.14159); + dos.writeUTF("Hello Binary"); + dos.writeBoolean(true); + + System.out.println("二进制文件写入完成"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 读取二进制文件 + public static void readBinaryFile() { + try (FileInputStream fis = new FileInputStream("data.bin"); + BufferedInputStream bis = new BufferedInputStream(fis); + DataInputStream dis = new DataInputStream(bis)) { + + // 按写入顺序读取数据 + int intValue = dis.readInt(); + double doubleValue = dis.readDouble(); + String stringValue = dis.readUTF(); + boolean booleanValue = dis.readBoolean(); + + System.out.println("读取的数据:"); + System.out.println("int: " + intValue); + System.out.println("double: " + doubleValue); + System.out.println("String: " + stringValue); + System.out.println("boolean: " + booleanValue); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 复制二进制文件 + public static void copyBinaryFile() { + try (FileInputStream fis = new FileInputStream("data.bin"); + FileOutputStream fos = new FileOutputStream("data_copy.bin"); + BufferedInputStream bis = new BufferedInputStream(fis); + BufferedOutputStream bos = new BufferedOutputStream(fos)) { + + byte[] buffer = new byte[1024]; + int bytesRead; + + while ((bytesRead = bis.read(buffer)) != -1) { + bos.write(buffer, 0, bytesRead); + } + + System.out.println("二进制文件复制完成"); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +### 13.4 NIO文件操作 +```java +import java.nio.file.*; +import java.nio.charset.StandardCharsets; +import java.io.IOException; +import java.util.List; + +public class NIOFileDemo { + public static void main(String[] args) { + try { + // 使用Path操作文件 + Path filePath = Paths.get("nio_test.txt"); + Path dirPath = Paths.get("nio_dir"); + + // 写入文件 + String content = "第一行\n第二行\n第三行"; + Files.write(filePath, content.getBytes(StandardCharsets.UTF_8)); + System.out.println("文件写入完成"); + + // 读取文件 + List lines = Files.readAllLines(filePath, StandardCharsets.UTF_8); + System.out.println("文件内容:"); + for (String line : lines) { + System.out.println(" " + line); + } + + // 文件信息 + System.out.println("文件大小: " + Files.size(filePath) + " 字节"); + System.out.println("文件存在: " + Files.exists(filePath)); + System.out.println("是否为目录: " + Files.isDirectory(filePath)); + + // 创建目录 + if (!Files.exists(dirPath)) { + Files.createDirectory(dirPath); + System.out.println("目录创建完成"); + } + + // 复制文件 + Path copyPath = dirPath.resolve("copy.txt"); + Files.copy(filePath, copyPath, StandardCopyOption.REPLACE_EXISTING); + System.out.println("文件复制完成"); + + // 移动文件 + Path movePath = dirPath.resolve("moved.txt"); + Files.move(copyPath, movePath, StandardCopyOption.REPLACE_EXISTING); + System.out.println("文件移动完成"); + + // 删除文件 + Files.deleteIfExists(movePath); + System.out.println("文件删除完成"); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +--- + +## 多线程 + +### 14.1 创建线程 +```java +// 方法1:继承Thread类 +class MyThread extends Thread { + private String threadName; + + public MyThread(String name) { + this.threadName = name; + } + + @Override + public void run() { + for (int i = 1; i <= 5; i++) { + System.out.println(threadName + " - 计数: " + i); + try { + Thread.sleep(1000); // 暂停1秒 + } catch (InterruptedException e) { + System.out.println(threadName + " 被中断"); + return; + } + } + System.out.println(threadName + " 执行完成"); + } +} + +// 方法2:实现Runnable接口 +class MyRunnable implements Runnable { + private String taskName; + + public MyRunnable(String name) { + this.taskName = name; + } + + @Override + public void run() { + for (int i = 1; i <= 5; i++) { + System.out.println(taskName + " - 执行: " + i); + try { + Thread.sleep(800); + } catch (InterruptedException e) { + System.out.println(taskName + " 被中断"); + return; + } + } + System.out.println(taskName + " 任务完成"); + } +} + +public class ThreadDemo { + public static void main(String[] args) { + // 使用Thread类 + MyThread thread1 = new MyThread("线程1"); + MyThread thread2 = new MyThread("线程2"); + + thread1.start(); // 启动线程 + thread2.start(); + + // 使用Runnable接口 + Thread thread3 = new Thread(new MyRunnable("任务A")); + Thread thread4 = new Thread(new MyRunnable("任务B")); + + thread3.start(); + thread4.start(); + + // 使用匿名类 + Thread thread5 = new Thread(() -> { + for (int i = 1; i <= 3; i++) { + System.out.println("Lambda线程 - " + i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + break; + } + } + }); + thread5.start(); + + // 等待所有线程完成 + try { + thread1.join(); + thread2.join(); + thread3.join(); + thread4.join(); + thread5.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + System.out.println("所有线程执行完成"); + } +} +``` + +### 14.2 线程同步 +```java +// 同步问题示例 +class Counter { + private int count = 0; + + // 非同步方法(可能出现并发问题) + public void incrementUnsafe() { + count++; + } + + // 同步方法 + public synchronized void increment() { + count++; + } + + // 同步代码块 + public void incrementWithBlock() { + synchronized (this) { + count++; + } + } + + public synchronized int getCount() { + return count; + } +} + +public class SynchronizationDemo { + public static void main(String[] args) throws InterruptedException { + Counter counter = new Counter(); + + // 创建多个线程同时操作计数器 + Thread[] threads = new Thread[10]; + + for (int i = 0; i < 10; i++) { + threads[i] = new Thread(() -> { + for (int j = 0; j < 1000; j++) { + counter.increment(); // 使用同步方法 + } + }); + threads[i].start(); + } + + // 等待所有线程完成 + for (Thread thread : threads) { + thread.join(); + } + + System.out.println("最终计数: " + counter.getCount()); + // 期望结果:10000 + } +} +``` + +### 14.3 线程通信 +```java +// 生产者-消费者模式 +class SharedBuffer { + private int[] buffer; + private int count = 0; + private int in = 0; + private int out = 0; + + public SharedBuffer(int size) { + buffer = new int[size]; + } + + // 生产者方法 + public synchronized void produce(int item) throws InterruptedException { + while (count == buffer.length) { + wait(); // 缓冲区满,等待 + } + + buffer[in] = item; + in = (in + 1) % buffer.length; + count++; + + System.out.println("生产: " + item + ",缓冲区数量: " + count); + notifyAll(); // 通知消费者 + } + + // 消费者方法 + public synchronized int consume() throws InterruptedException { + while (count == 0) { + wait(); // 缓冲区空,等待 + } + + int item = buffer[out]; + out = (out + 1) % buffer.length; + count--; + + System.out.println("消费: " + item + ",缓冲区数量: " + count); + notifyAll(); // 通知生产者 + + return item; + } +} + +class Producer extends Thread { + private SharedBuffer buffer; + + public Producer(SharedBuffer buffer) { + this.buffer = buffer; + } + + @Override + public void run() { + try { + for (int i = 1; i <= 10; i++) { + buffer.produce(i); + Thread.sleep(100); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} + +class Consumer extends Thread { + private SharedBuffer buffer; + + public Consumer(SharedBuffer buffer) { + this.buffer = buffer; + } + + @Override + public void run() { + try { + for (int i = 1; i <= 10; i++) { + buffer.consume(); + Thread.sleep(150); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} + +public class ProducerConsumerDemo { + public static void main(String[] args) throws InterruptedException { + SharedBuffer buffer = new SharedBuffer(5); + + Producer producer = new Producer(buffer); + Consumer consumer = new Consumer(buffer); + + producer.start(); + consumer.start(); + + producer.join(); + consumer.join(); + + System.out.println("生产者-消费者演示完成"); + } +} +``` + +### 14.4 线程池 +```java +import java.util.concurrent.*; + +public class ThreadPoolDemo { + public static void main(String[] args) { + // 固定大小线程池 + ExecutorService fixedPool = Executors.newFixedThreadPool(3); + + // 提交任务 + for (int i = 1; i <= 10; i++) { + final int taskId = i; + fixedPool.submit(() -> { + System.out.println("任务" + taskId + " 由线程 " + + Thread.currentThread().getName() + " 执行"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + } + + // 关闭线程池 + fixedPool.shutdown(); + + // 缓存线程池 + ExecutorService cachedPool = Executors.newCachedThreadPool(); + + // 单线程线程池 + ExecutorService singlePool = Executors.newSingleThreadExecutor(); + + // 定时任务线程池 + ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2); + + // 延迟执行 + scheduledPool.schedule(() -> { + System.out.println("延迟任务执行"); + }, 2, TimeUnit.SECONDS); + + // 定期执行 + scheduledPool.scheduleAtFixedRate(() -> { + System.out.println("定期任务: " + System.currentTimeMillis()); + }, 1, 3, TimeUnit.SECONDS); + + // 使用Future获取结果 + Future future = fixedPool.submit(() -> { + Thread.sleep(2000); + return 42; + }); + + try { + Integer result = future.get(3, TimeUnit.SECONDS); + System.out.println("任务结果: " + result); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + + // 关闭所有线程池 + try { + if (!fixedPool.awaitTermination(60, TimeUnit.SECONDS)) { + fixedPool.shutdownNow(); + } + } catch (InterruptedException e) { + fixedPool.shutdownNow(); + } + + scheduledPool.shutdown(); + } +} +``` + +--- + +## 泛型 + +### 15.1 泛型基础 +```java +// 泛型类 +public class GenericBox { + private T content; + + public GenericBox(T content) { + this.content = content; + } + + public T getContent() { + return content; + } + + public void setContent(T content) { + this.content = content; + } + + @Override + public String toString() { + return "Box[" + content + "]"; + } +} + +// 泛型接口 +public interface GenericComparable { + int compare(T other); +} + +// 实现泛型接口 +public class Person implements GenericComparable { + private String name; + private int age; + + public Person(String name, int age) { + this.name = name; + this.age = age; + } + + @Override + public int compare(Person other) { + return Integer.compare(this.age, other.age); + } + + // getters和toString... +} + +public class GenericDemo { + public static void main(String[] args) { + // 使用泛型类 + GenericBox stringBox = new GenericBox<>("Hello"); + System.out.println(stringBox.getContent()); + + GenericBox intBox = new GenericBox<>(42); + System.out.println(intBox.getContent()); + + // 钻石操作符(Java 7+) + GenericBox doubleBox = new GenericBox<>(3.14); + + // 泛型方法调用 + String[] strings = {"apple", "banana", "cherry"}; + String maxString = findMax(strings); + System.out.println("最大字符串: " + maxString); + + Integer[] numbers = {5, 2, 8, 1, 9}; + Integer maxNumber = findMax(numbers); + System.out.println("最大数字: " + maxNumber); + } + + // 泛型方法 + public static > T findMax(T[] array) { + if (array == null || array.length == 0) { + return null; + } + + T max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i].compareTo(max) > 0) { + max = array[i]; + } + } + return max; + } +} +``` + +### 15.2 泛型通配符 +```java +import java.util.*; + +public class WildcardDemo { + public static void main(String[] args) { + List intList = Arrays.asList(1, 2, 3, 4, 5); + List doubleList = Arrays.asList(1.1, 2.2, 3.3); + List stringList = Arrays.asList("a", "b", "c"); + + // 上界通配符 + printNumbers(intList); + printNumbers(doubleList); + // printNumbers(stringList); // 编译错误,String不是Number的子类 + + // 下界通配符 + List numberList = new ArrayList<>(); + addNumbers(numberList); + System.out.println("Number列表: " + numberList); + + // 无界通配符 + printListSize(intList); + printListSize(stringList); + + // 泛型限制示例 + GenericRestrictionDemo(); + } + + // 上界通配符:? extends Number + public static void printNumbers(List list) { + for (Number num : list) { + System.out.println("数字: " + num.doubleValue()); + } + } + + // 下界通配符:? super Integer + public static void addNumbers(List list) { + list.add(1); + list.add(2); + list.add(3); + } + + // 无界通配符:? + public static void printListSize(List list) { + System.out.println("列表大小: " + list.size()); + } + + public static void GenericRestrictionDemo() { + // 泛型擦除示例 + List stringList = new ArrayList<>(); + List integerList = new ArrayList<>(); + + // 运行时类型相同 + System.out.println("类型相同: " + + (stringList.getClass() == integerList.getClass())); + + // 多重限制 + processComparableAndSerializable("Hello"); + processComparableAndSerializable(42); + } + + // 多重限制:T必须既实现Comparable又实现Serializable + public static & java.io.Serializable> void + processComparableAndSerializable(T item) { + System.out.println("处理项目: " + item); + System.out.println("可比较: " + (item instanceof Comparable)); + System.out.println("可序列化: " + (item instanceof java.io.Serializable)); + } +} +``` + +--- + +## 注解 + +### 16.1 内置注解 +```java +// @Override:标记重写方法 +class Animal { + public void makeSound() { + System.out.println("动物发出声音"); + } +} + +class Dog extends Animal { + @Override + public void makeSound() { + System.out.println("狗汪汪叫"); + } +} + +// @Deprecated:标记过时的方法 +class Calculator { + @Deprecated + public int oldAdd(int a, int b) { + return a + b; + } + + public int add(int a, int b) { + return a + b; + } +} + +// @SuppressWarnings:抑制警告 +public class AnnotationDemo { + @SuppressWarnings("unchecked") + public static void rawTypeExample() { + List list = new ArrayList(); // 原始类型,会有警告 + list.add("hello"); + list.add(123); + } + + @SuppressWarnings({"unused", "deprecation"}) + public static void suppressMultipleWarnings() { + int unusedVariable = 10; + Calculator calc = new Calculator(); + int result = calc.oldAdd(1, 2); // 使用过时方法 + } +} +``` + +### 16.2 自定义注解 +```java +import java.lang.annotation.*; + +// 定义注解 +@Target(ElementType.METHOD) // 作用于方法 +@Retention(RetentionPolicy.RUNTIME) // 运行时可获取 +@Documented // 包含在javadoc中 +public @interface TestMethod { + String description() default ""; + int timeout() default 5000; + boolean enabled() default true; +} + +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Author { + String name(); + String email() default ""; + String date(); +} + +// 使用自定义注解 +@Author(name = "张三", email = "zhangsan@example.com", date = "2024-01-01") +public class MyTestClass { + + @TestMethod(description = "测试加法运算", timeout = 3000) + public void testAdd() { + System.out.println("执行加法测试"); + } + + @TestMethod(description = "测试除法运算", enabled = false) + public void testDivide() { + System.out.println("执行除法测试"); + } + + @TestMethod + public void testDefault() { + System.out.println("使用默认参数的测试"); + } + + public void normalMethod() { + System.out.println("普通方法,没有注解"); + } +} +``` + +### 16.3 注解处理 +```java +import java.lang.reflect.*; + +public class AnnotationProcessor { + public static void main(String[] args) { + processClass(MyTestClass.class); + } + + public static void processClass(Class clazz) { + // 处理类级别注解 + if (clazz.isAnnotationPresent(Author.class)) { + Author author = clazz.getAnnotation(Author.class); + System.out.println("作者信息:"); + System.out.println(" 姓名: " + author.name()); + System.out.println(" 邮箱: " + author.email()); + System.out.println(" 日期: " + author.date()); + System.out.println(); + } + + // 处理方法级别注解 + Method[] methods = clazz.getDeclaredMethods(); + + for (Method method : methods) { + if (method.isAnnotationPresent(TestMethod.class)) { + TestMethod testMethod = method.getAnnotation(TestMethod.class); + + System.out.println("测试方法: " + method.getName()); + System.out.println(" 描述: " + testMethod.description()); + System.out.println(" 超时: " + testMethod.timeout() + "ms"); + System.out.println(" 启用: " + testMethod.enabled()); + + if (testMethod.enabled()) { + try { + // 创建实例并调用方法 + Object instance = clazz.getDeclaredConstructor().newInstance(); + method.invoke(instance); + System.out.println(" 执行结果: 成功"); + } catch (Exception e) { + System.out.println(" 执行结果: 失败 - " + e.getMessage()); + } + } else { + System.out.println(" 执行结果: 跳过(已禁用)"); + } + System.out.println(); + } + } + } +} +``` + +--- + +## Lambda表达式 + +### 17.1 Lambda基础语法 +```java +import java.util.*; +import java.util.function.*; + +public class LambdaBasics { + public static void main(String[] args) { + // 1. 无参数Lambda + Runnable task1 = () -> System.out.println("Hello Lambda!"); + task1.run(); + + // 2. 单参数Lambda(可省略括号) + Consumer printer = message -> System.out.println("消息: " + message); + printer.accept("Lambda表达式"); + + // 3. 多参数Lambda + BinaryOperator adder = (a, b) -> a + b; + System.out.println("5 + 3 = " + adder.apply(5, 3)); + + // 4. 有返回值的Lambda + Function lengthCalculator = str -> str.length(); + System.out.println("字符串长度: " + lengthCalculator.apply("Hello")); + + // 5. 多行Lambda + Function, Integer> sumCalculator = list -> { + int sum = 0; + for (int num : list) { + sum += num; + } + return sum; + }; + + List numbers = Arrays.asList(1, 2, 3, 4, 5); + System.out.println("列表总和: " + sumCalculator.apply(numbers)); + + // 6. 方法引用 + List words = Arrays.asList("apple", "banana", "cherry"); + + // Lambda表达式 + words.forEach(word -> System.out.println(word)); + + // 方法引用(等价) + words.forEach(System.out::println); + + // 静态方法引用 + Function parseInt = Integer::parseInt; + System.out.println("解析数字: " + parseInt.apply("42")); + + // 实例方法引用 + String text = "Hello World"; + Supplier upperCase = text::toUpperCase; + System.out.println("大写: " + upperCase.get()); + + // 构造方法引用 + Supplier> listSupplier = ArrayList::new; + List newList = listSupplier.get(); + } +} +``` + +### 17.2 函数式接口 +```java +// 自定义函数式接口 +@FunctionalInterface +public interface MathOperation { + int operate(int a, int b); + + // 可以有默认方法 + default void printResult(int a, int b) { + System.out.println("结果: " + operate(a, b)); + } + + // 可以有静态方法 + static void printInfo() { + System.out.println("这是一个数学运算接口"); + } +} + +// 内置函数式接口使用 +import java.util.function.*; + +public class FunctionalInterfaceDemo { + public static void main(String[] args) { + // Consumer:接受一个参数,无返回值 + Consumer stringConsumer = str -> System.out.println("处理: " + str); + stringConsumer.accept("测试字符串"); + + // Supplier:无参数,返回一个值 + Supplier randomSupplier = () -> Math.random(); + System.out.println("随机数: " + randomSupplier.get()); + + // Function:接受一个参数,返回一个值 + Function stringLength = String::length; + System.out.println("长度: " + stringLength.apply("Hello")); + + // Predicate:接受一个参数,返回boolean + Predicate isEven = num -> num % 2 == 0; + System.out.println("8是偶数: " + isEven.test(8)); + System.out.println("7是偶数: " + isEven.test(7)); + + // BiFunction:接受两个参数,返回一个值 + BiFunction concatenator = (s1, s2) -> s1 + " " + s2; + System.out.println("拼接: " + concatenator.apply("Hello", "World")); + + // UnaryOperator:接受一个参数,返回同类型值 + UnaryOperator upperCase = String::toUpperCase; + System.out.println("大写: " + upperCase.apply("hello")); + + // BinaryOperator:接受两个同类型参数,返回同类型值 + BinaryOperator maxOperator = Integer::max; + System.out.println("最大值: " + maxOperator.apply(10, 20)); + + // 使用自定义函数式接口 + MathOperation addition = (a, b) -> a + b; + MathOperation multiplication = (a, b) -> a * b; + + System.out.println("加法: " + calculate(5, 3, addition)); + System.out.println("乘法: " + calculate(5, 3, multiplication)); + + // Predicate组合 + Predicate isPositive = num -> num > 0; + Predicate isLessThan100 = num -> num < 100; + Predicate isInRange = isPositive.and(isLessThan100); + + System.out.println("50在范围内: " + isInRange.test(50)); + System.out.println("-5在范围内: " + isInRange.test(-5)); + System.out.println("150在范围内: " + isInRange.test(150)); + } + + public static int calculate(int a, int b, MathOperation operation) { + return operation.operate(a, b); + } +} +``` + +--- + +## Stream API + +### 18.1 Stream基础操作 +```java +import java.util.*; +import java.util.stream.*; + +public class StreamBasics { + public static void main(String[] args) { + List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + // 1. 创建Stream + // 从集合创建 + Stream stream1 = numbers.stream(); + + // 从数组创建 + String[] words = {"apple", "banana", "cherry"}; + Stream stream2 = Arrays.stream(words); + + // 使用Stream.of()创建 + Stream stream3 = Stream.of("a", "b", "c"); + + // 生成Stream + Stream infiniteStream = Stream.iterate(0, n -> n + 2).limit(10); + Stream randomStream = Stream.generate(Math::random).limit(5); + + // 2. 中间操作(Intermediate Operations) + + // filter:过滤 + List evenNumbers = numbers.stream() + .filter(n -> n % 2 == 0) + .collect(Collectors.toList()); + System.out.println("偶数: " + evenNumbers); + + // map:转换 + List squareStrings = numbers.stream() + .map(n -> n * n) + .map(String::valueOf) + .collect(Collectors.toList()); + System.out.println("平方字符串: " + squareStrings); + + // distinct:去重 + List duplicates = Arrays.asList(1, 2, 2, 3, 3, 3, 4); + List unique = duplicates.stream() + .distinct() + .collect(Collectors.toList()); + System.out.println("去重: " + unique); + + // sorted:排序 + List fruits = Arrays.asList("banana", "apple", "cherry", "date"); + List sortedFruits = fruits.stream() + .sorted() + .collect(Collectors.toList()); + System.out.println("排序: " + sortedFruits); + + // 自定义排序 + List sortedByLength = fruits.stream() + .sorted(Comparator.comparing(String::length)) + .collect(Collectors.toList()); + System.out.println("按长度排序: " + sortedByLength); + + // limit和skip:限制和跳过 + List limitedNumbers = numbers.stream() + .skip(3) + .limit(4) + .collect(Collectors.toList()); + System.out.println("跳过3个,取4个: " + limitedNumbers); + + // 3. 终端操作(Terminal Operations) + + // forEach:遍历 + System.out.print("遍历输出: "); + numbers.stream() + .filter(n -> n <= 5) + .forEach(n -> System.out.print(n + " ")); + System.out.println(); + + // collect:收集 + Set numberSet = numbers.stream() + .collect(Collectors.toSet()); + System.out.println("转为Set: " + numberSet); + + // reduce:归约 + Optional sum = numbers.stream() + .reduce(Integer::sum); + System.out.println("总和: " + sum.orElse(0)); + + int product = numbers.stream() + .reduce(1, (a, b) -> a * b); + System.out.println("乘积: " + product); + + // count:计数 + long count = numbers.stream() + .filter(n -> n > 5) + .count(); + System.out.println("大于5的数量: " + count); + + // anyMatch, allMatch, noneMatch:匹配 + boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0); + boolean allPositive = numbers.stream().allMatch(n -> n > 0); + boolean noneNegative = numbers.stream().noneMatch(n -> n < 0); + + System.out.println("有偶数: " + hasEven); + System.out.println("全为正数: " + allPositive); + System.out.println("没有负数: " + noneNegative); + + // findFirst, findAny:查找 + Optional first = numbers.stream() + .filter(n -> n > 5) + .findFirst(); + System.out.println("第一个大于5: " + first.orElse(-1)); + + // min, max:最值 + Optional min = numbers.stream().min(Integer::compareTo); + Optional max = numbers.stream().max(Integer::compareTo); + System.out.println("最小值: " + min.orElse(-1)); + System.out.println("最大值: " + max.orElse(-1)); + } +} +``` + +### 18.2 复杂Stream操作 +```java +// 学生类 +class Student { + private String name; + private int age; + private String gender; + private double score; + private String subject; + + public Student(String name, int age, String gender, double score, String subject) { + this.name = name; + this.age = age; + this.gender = gender; + this.score = score; + this.subject = subject; + } + + // getters和toString方法... + public String getName() { return name; } + public int getAge() { return age; } + public String getGender() { return gender; } + public double getScore() { return score; } + public String getSubject() { return subject; } + + @Override + public String toString() { + return String.format("Student{name='%s', age=%d, gender='%s', score=%.1f, subject='%s'}", + name, age, gender, score, subject); + } +} + +public class AdvancedStreamOperations { + public static void main(String[] args) { + List students = Arrays.asList( + new Student("张三", 20, "男", 85.5, "数学"), + new Student("李四", 21, "女", 92.0, "英语"), + new Student("王五", 19, "男", 78.5, "数学"), + new Student("赵六", 22, "女", 88.0, "物理"), + new Student("陈七", 20, "男", 95.5, "英语"), + new Student("刘八", 21, "女", 82.0, "物理") + ); + + // 1. 分组操作 + Map> bySubject = students.stream() + .collect(Collectors.groupingBy(Student::getSubject)); + + System.out.println("按科目分组:"); + bySubject.forEach((subject, studentList) -> { + System.out.println(subject + ": " + studentList.size() + "人"); + }); + + // 2. 分组并统计 + Map avgScoreBySubject = students.stream() + .collect(Collectors.groupingBy( + Student::getSubject, + Collectors.averagingDouble(Student::getScore) + )); + + System.out.println("\n各科目平均分:"); + avgScoreBySubject.forEach((subject, avgScore) -> + System.out.printf("%s: %.2f\n", subject, avgScore)); + + // 3. 分区操作 + Map> partitionByGender = students.stream() + .collect(Collectors.partitioningBy(s -> "男".equals(s.getGender()))); + + System.out.println("\n按性别分区:"); + System.out.println("男生: " + partitionByGender.get(true).size() + "人"); + System.out.println("女生: " + partitionByGender.get(false).size() + "人"); + + // 4. 复杂统计 + DoubleSummaryStatistics scoreStats = students.stream() + .mapToDouble(Student::getScore) + .summaryStatistics(); + + System.out.println("\n成绩统计:"); + System.out.printf("平均分: %.2f\n", scoreStats.getAverage()); + System.out.printf("最高分: %.1f\n", scoreStats.getMax()); + System.out.printf("最低分: %.1f\n", scoreStats.getMin()); + System.out.printf("总分: %.1f\n", scoreStats.getSum()); + System.out.println("人数: " + scoreStats.getCount()); + + // 5. 多级分组 + Map>> multiGroup = students.stream() + .collect(Collectors.groupingBy( + Student::getSubject, + Collectors.groupingBy(Student::getGender) + )); + + System.out.println("\n多级分组(科目-性别):"); + multiGroup.forEach((subject, genderMap) -> { + System.out.println(subject + ":"); + genderMap.forEach((gender, studentList) -> + System.out.println(" " + gender + ": " + studentList.size() + "人")); + }); + + // 6. 自定义收集器 + String studentNames = students.stream() + .map(Student::getName) + .collect(Collectors.joining(", ", "[", "]")); + System.out.println("\n学生姓名: " + studentNames); + + // 7. 排序和查找 + System.out.println("\n成绩前3名:"); + students.stream() + .sorted(Comparator.comparing(Student::getScore).reversed()) + .limit(3) + .forEach(System.out::println); + + // 8. 复合条件查询 + System.out.println("\n数学科目且成绩大于80分的学生:"); + students.stream() + .filter(s -> "数学".equals(s.getSubject())) + .filter(s -> s.getScore() > 80) + .forEach(System.out::println); + + // 9. flatMap操作 + List> nestedList = Arrays.asList( + Arrays.asList("a", "b"), + Arrays.asList("c", "d", "e"), + Arrays.asList("f") + ); + + List flatList = nestedList.stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + System.out.println("\n扁平化列表: " + flatList); + + // 10. 并行流 + long parallelSum = students.parallelStream() + .mapToDouble(Student::getScore) + .sum(); + System.out.println("\n并行计算总分: " + parallelSum); + } +} +``` + +--- + +## 总结 + +这份Java语法指南涵盖了Java编程的核心概念和高级特性: + +**基础部分**: +- 数据类型、变量、运算符 +- 控制结构、数组、方法 + +**面向对象**: +- 类与对象、封装、继承、多态 +- 接口与抽象类 + +**高级特性**: +- 异常处理、集合框架、IO操作 +- 多线程、泛型、注解 +- Lambda表达式、Stream API + +**学习建议**: +1. **循序渐进**:从基础语法开始,逐步掌握高级特性 +2. **多加练习**:通过编写代码加深理解 +3. **项目实战**:将知识点应用到实际项目中 +4. **持续学习**:Java生态系统丰富,需要不断学习新技术 + +Java是一门功能强大的编程语言,掌握这些语法特性将为你的编程之路打下坚实基础! \ No newline at end of file diff --git a/Linux常用命令详解.md b/Linux常用命令详解.md new file mode 100644 index 0000000..fcac0f7 --- /dev/null +++ b/Linux常用命令详解.md @@ -0,0 +1,1997 @@ +# Linux 常用命令详解 + +## 目录 +1. [文件和目录操作](#文件和目录操作) +2. [文本处理命令](#文本处理命令) +3. [系统信息和进程管理](#系统信息和进程管理) +4. [网络命令](#网络命令) +5. [权限和用户管理](#权限和用户管理) +6. [压缩和归档](#压缩和归档) +7. [查找和搜索](#查找和搜索) +8. [系统监控](#系统监控) + +--- + +## 文件和目录操作 + +### ls - 列出目录内容 + +**基本语法:** `ls [选项] [目录]` + +**常用选项:** +- `-l` 详细信息(长格式) +- `-a` 显示隐藏文件 +- `-h` 人类可读的文件大小 +- `-t` 按修改时间排序 +- `-r` 反向排序 +- `-R` 递归显示子目录 + +**实例:** +```bash +# 基本用法 +ls +# 输出:Documents Downloads Music Pictures Videos + +# 详细信息显示 +ls -l +# 输出: +# drwxr-xr-x 2 user user 4096 Dec 24 10:30 Documents +# drwxr-xr-x 2 user user 4096 Dec 24 09:15 Downloads +# -rw-r--r-- 1 user user 1024 Dec 24 08:45 readme.txt + +# 显示所有文件(包括隐藏文件) +ls -la +# 输出: +# total 24 +# drwxr-xr-x 3 user user 4096 Dec 24 10:30 . +# drwxr-xr-x 3 root root 4096 Dec 23 15:20 .. +# -rw------- 1 user user 220 Dec 23 15:20 .bash_logout +# -rw------- 1 user user 3771 Dec 23 15:20 .bashrc + +# 按大小排序,人类可读格式 +ls -lhS +# 输出: +# -rw-r--r-- 1 user user 2.1M Dec 24 10:30 large_file.zip +# -rw-r--r-- 1 user user 512K Dec 24 09:15 medium_file.pdf +# -rw-r--r-- 1 user user 1.0K Dec 24 08:45 small_file.txt +``` + +### cd - 切换目录 + +**基本语法:** `cd [目录路径]` + +**特殊符号:** +- `~` 用户主目录 +- `-` 上一个目录 +- `..` 父目录 +- `.` 当前目录 + +**实例:** +```bash +# 切换到用户主目录 +cd ~ +pwd +# 输出:/home/username + +# 切换到根目录 +cd / +pwd +# 输出:/ + +# 切换到上级目录 +cd .. +pwd +# 输出:/ + +# 切换到上一个目录 +cd /home/username/Documents +cd /tmp +cd - +pwd +# 输出:/home/username/Documents + +# 使用相对路径 +cd ./subfolder/another_folder +``` + +### mkdir - 创建目录 + +**基本语法:** `mkdir [选项] 目录名` + +**常用选项:** +- `-p` 创建多级目录 +- `-m` 设置权限模式 + +**实例:** +```bash +# 创建单个目录 +mkdir new_folder + +# 创建多级目录 +mkdir -p project/src/components/ui +# 创建了:project/src/components/ui 整个路径 + +# 创建多个目录 +mkdir folder1 folder2 folder3 + +# 创建目录并设置权限 +mkdir -m 755 public_folder +mkdir -m 700 private_folder + +# 验证创建结果 +ls -la +# 输出: +# drwxr-xr-x 2 user user 4096 Dec 24 11:00 public_folder +# drwx------ 2 user user 4096 Dec 24 11:00 private_folder +``` + +### cp - 复制文件和目录 + +**基本语法:** `cp [选项] 源文件 目标文件` + +**常用选项:** +- `-r` 递归复制目录 +- `-i` 交互式复制(覆盖前询问) +- `-v` 显示详细过程 +- `-a` 保持所有属性 + +**实例:** +```bash +# 复制文件 +cp file1.txt file2.txt + +# 复制文件到目录 +cp file1.txt /home/user/backup/ + +# 递归复制目录 +cp -r source_folder/ destination_folder/ + +# 交互式复制(防止意外覆盖) +cp -i important.txt backup_important.txt +# 输出:cp: overwrite 'backup_important.txt'? y + +# 保持所有属性复制 +cp -a original_folder/ backup_folder/ + +# 复制多个文件到目录 +cp file1.txt file2.txt file3.txt /backup/ +``` + +### mv - 移动/重命名文件和目录 + +**基本语法:** `mv [选项] 源文件 目标文件` + +**常用选项:** +- `-i` 交互式移动 +- `-v` 显示详细过程 +- `-u` 只在源文件更新时移动 + +**实例:** +```bash +# 重命名文件 +mv oldname.txt newname.txt + +# 移动文件到目录 +mv file.txt /home/user/documents/ + +# 移动并重命名 +mv old_file.txt /new/location/new_name.txt + +# 移动目录 +mv old_folder/ /new/location/ + +# 批量移动文件 +mv *.txt /text_files/ + +# 交互式移动 +mv -i file.txt existing_file.txt +# 输出:mv: overwrite 'existing_file.txt'? n +``` + +### rm - 删除文件和目录 + +**基本语法:** `rm [选项] 文件名` + +**常用选项:** +- `-r` 递归删除目录 +- `-f` 强制删除,不询问 +- `-i` 交互式删除 +- `-v` 显示详细过程 + +**实例:** +```bash +# 删除文件 +rm file.txt + +# 交互式删除 +rm -i important.txt +# 输出:rm: remove regular file 'important.txt'? y + +# 删除目录及其内容 +rm -r folder/ + +# 强制删除(危险操作) +rm -rf unwanted_folder/ + +# 删除特定类型文件 +rm *.tmp +rm -f *.log + +# 显示删除过程 +rm -v file1.txt file2.txt +# 输出: +# removed 'file1.txt' +# removed 'file2.txt' +``` + +--- + +## 文本处理命令 + +### cat - 显示文件内容 + +**基本语法:** `cat [选项] 文件名` + +**常用选项:** +- `-n` 显示行号 +- `-b` 显示非空行行号 +- `-s` 压缩空行 +- `-A` 显示所有字符 + +**实例:** +```bash +# 创建示例文件 +echo -e "第一行\n\n第三行\n第四行" > sample.txt + +# 基本显示 +cat sample.txt +# 输出: +# 第一行 +# +# 第三行 +# 第四行 + +# 显示行号 +cat -n sample.txt +# 输出: +# 1 第一行 +# 2 +# 3 第三行 +# 4 第四行 + +# 只显示非空行号 +cat -b sample.txt +# 输出: +# 1 第一行 +# +# 2 第三行 +# 3 第四行 + +# 连接多个文件 +cat file1.txt file2.txt > combined.txt + +# 创建文件(Ctrl+D结束输入) +cat > newfile.txt +# 输入内容后按Ctrl+D保存 +``` + +### grep - 文本搜索 + +**基本语法:** `grep [选项] 模式 文件名` + +**常用选项:** +- `-i` 忽略大小写 +- `-v` 反向匹配(不包含) +- `-n` 显示行号 +- `-r` 递归搜索 +- `-c` 只显示匹配行数 + +**实例:** +```bash +# 创建示例文件 +cat > employees.txt << EOF +John Smith, Manager, 50000 +Jane Doe, Developer, 45000 +Bob Johnson, Manager, 55000 +Alice Brown, Designer, 40000 +Mike Wilson, Developer, 48000 +EOF + +# 基本搜索 +grep "Manager" employees.txt +# 输出: +# John Smith, Manager, 50000 +# Bob Johnson, Manager, 55000 + +# 忽略大小写搜索 +grep -i "manager" employees.txt +# 同上输出 + +# 显示行号 +grep -n "Developer" employees.txt +# 输出: +# 2:Jane Doe, Developer, 45000 +# 5:Mike Wilson, Developer, 48000 + +# 反向匹配 +grep -v "Manager" employees.txt +# 输出: +# Jane Doe, Developer, 45000 +# Alice Brown, Designer, 40000 +# Mike Wilson, Developer, 48000 + +# 计算匹配行数 +grep -c "Developer" employees.txt +# 输出:2 + +# 使用正则表达式 +grep "^[JM]" employees.txt # 以J或M开头的行 +# 输出: +# John Smith, Manager, 50000 +# Mike Wilson, Developer, 48000 + +# 递归搜索目录 +grep -r "function" /path/to/code/ +``` + +### sed - 流编辑器 + +**基本语法:** `sed [选项] '命令' 文件名` + +**常用命令:** +- `s/old/new/` 替换 +- `d` 删除行 +- `p` 打印行 +- `a` 追加行 + +**实例:** +```bash +# 创建示例文件 +cat > data.txt << EOF +Hello World +This is line 2 +Hello Universe +This is line 4 +EOF + +# 基本替换(只替换每行第一个) +sed 's/Hello/Hi/' data.txt +# 输出: +# Hi World +# This is line 2 +# Hi Universe +# This is line 4 + +# 全局替换 +sed 's/is/was/g' data.txt +# 输出: +# Hello World +# Thwas was line 2 +# Hello Universe +# Thwas was line 4 + +# 删除包含特定文本的行 +sed '/line 2/d' data.txt +# 输出: +# Hello World +# Hello Universe +# This is line 4 + +# 打印特定行 +sed -n '2,3p' data.txt +# 输出: +# This is line 2 +# Hello Universe + +# 在特定行后添加内容 +sed '2a\This is an added line' data.txt +# 输出: +# Hello World +# This is line 2 +# This is an added line +# Hello Universe +# This is line 4 + +# 直接修改文件 +sed -i 's/Hello/Hi/g' data.txt +``` + +### awk - 文本处理工具 + +**基本语法:** `awk 'pattern { action }' 文件名` + +**内置变量:** +- `NR` 当前行号 +- `NF` 当前行字段数 +- `$0` 整行内容 +- `$1,$2,...` 各字段内容 + +**实例:** +```bash +# 创建示例数据 +cat > sales.txt << EOF +Product Price Quantity +Apple 1.20 100 +Banana 0.80 150 +Orange 1.50 80 +Grape 2.00 60 +EOF + +# 打印所有行 +awk '{print}' sales.txt +# 或 +awk '{print $0}' sales.txt + +# 打印特定字段 +awk '{print $1, $2}' sales.txt +# 输出: +# Product Price +# Apple 1.20 +# Banana 0.80 +# Orange 1.50 +# Grape 2.00 + +# 计算总价 +awk 'NR>1 {total += $2 * $3} END {print "Total:", total}' sales.txt +# 输出:Total: 440 + +# 添加条件 +awk 'NR>1 && $2 > 1.00 {print $1, "is expensive"}' sales.txt +# 输出: +# Apple is expensive +# Orange is expensive +# Grape is expensive + +# 格式化输出 +awk 'NR>1 {printf "%-10s $%.2f\n", $1, $2}' sales.txt +# 输出: +# Apple $1.20 +# Banana $0.80 +# Orange $1.50 +# Grape $2.00 + +# 统计行数和字段数 +awk 'END {print "Lines:", NR, "Max fields:", NF}' sales.txt +# 输出:Lines: 5 Max fields: 3 +``` + +### sort - 排序文本 + +**基本语法:** `sort [选项] 文件名` + +**常用选项:** +- `-n` 数字排序 +- `-r` 反向排序 +- `-k` 指定字段排序 +- `-u` 去重排序 +- `-t` 指定分隔符 + +**实例:** +```bash +# 创建示例数据 +cat > students.txt << EOF +Alice,85,Math +Bob,92,Physics +Charlie,78,Math +David,95,Physics +Eve,88,Chemistry +EOF + +# 基本排序(按字母顺序) +sort students.txt +# 输出: +# Alice,85,Math +# Bob,92,Physics +# Charlie,78,Math +# David,95,Physics +# Eve,88,Chemistry + +# 按第二字段(分数)数字排序 +sort -t',' -k2 -n students.txt +# 输出: +# Charlie,78,Math +# Alice,85,Math +# Eve,88,Chemistry +# Bob,92,Physics +# David,95,Physics + +# 反向排序(分数从高到低) +sort -t',' -k2 -nr students.txt +# 输出: +# David,95,Physics +# Bob,92,Physics +# Eve,88,Chemistry +# Alice,85,Math +# Charlie,78,Math + +# 按学科排序,再按分数排序 +sort -t',' -k3,3 -k2,2n students.txt +# 输出: +# Eve,88,Chemistry +# Alice,85,Math +# Charlie,78,Math +# Bob,92,Physics +# David,95,Physics +``` + +--- + +## 系统信息和进程管理 + +### ps - 显示进程信息 + +**基本语法:** `ps [选项]` + +**常用选项:** +- `aux` 显示所有进程详细信息 +- `-ef` 显示所有进程完整信息 +- `-u` 按用户显示 +- `--forest` 树形显示 + +**实例:** +```bash +# 显示当前用户进程 +ps +# 输出: +# PID TTY TIME CMD +# 1234 pts/0 00:00:01 bash +# 5678 pts/0 00:00:00 ps + +# 显示所有进程详细信息 +ps aux | head -10 +# 输出: +# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND +# root 1 0.0 0.1 77616 8968 ? Ss Dec23 0:02 /sbin/init +# root 2 0.0 0.0 0 0 ? S Dec23 0:00 [kthreadd] +# root 3 0.0 0.0 0 0 ? I< Dec23 0:00 [rcu_gp] + +# 查找特定进程 +ps aux | grep firefox +# 输出: +# user 12345 2.1 5.4 2547696 437184 ? Sl 10:30 1:23 /usr/bin/firefox + +# 按CPU使用率排序 +ps aux --sort=-%cpu | head -5 +# 输出:显示CPU使用率最高的5个进程 + +# 按内存使用排序 +ps aux --sort=-%mem | head -5 +# 输出:显示内存使用最高的5个进程 + +# 显示进程树 +ps --forest +# 输出: +# PID TTY TIME CMD +# 1234 pts/0 00:00:01 bash +# 5678 pts/0 00:00:00 \_ ps +``` + +### top - 动态显示进程 + +**基本语法:** `top [选项]` + +**交互命令:** +- `q` 退出 +- `k` 终止进程 +- `M` 按内存排序 +- `P` 按CPU排序 +- `1` 显示所有CPU核心 + +**实例:** +```bash +# 基本使用 +top + +# 输出示例: +# top - 14:30:25 up 1 day, 3:45, 2 users, load average: 0.15, 0.10, 0.05 +# Tasks: 234 total, 1 running, 233 sleeping, 0 stopped, 0 zombie +# %Cpu(s): 2.3 us, 1.0 sy, 0.0 ni, 96.4 id, 0.3 wa, 0.0 hi, 0.0 si, 0.0 st +# MiB Mem : 7936.2 total, 2148.5 free, 3421.8 used, 2365.9 buff/cache +# MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 4102.5 avail Mem + +# PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND +# 1234 user 20 0 2547696 437184 123456 S 5.3 5.4 1:23.45 firefox +# 5678 user 20 0 987654 234567 98765 S 2.1 2.9 0:45.12 chrome + +# 只显示特定用户的进程 +top -u username + +# 设置刷新间隔(秒) +top -d 2 + +# 显示指定进程 +top -p 1234,5678 +``` + +### htop - 改进的top命令 + +**基本语法:** `htop [选项]` + +**交互键:** +- `F1` 帮助 +- `F2` 设置 +- `F3` 搜索 +- `F4` 过滤 +- `F5` 树形视图 +- `F6` 排序 +- `F9` 终止进程 +- `F10` 退出 + +**实例:** +```bash +# 基本使用(需要先安装:sudo apt install htop) +htop + +# htop界面显示: +# - CPU使用率条形图 +# - 内存和交换空间使用率 +# - 进程列表,支持颜色和交互操作 +# - 可以用鼠标操作 + +# 命令行选项 +htop -u username # 只显示指定用户进程 +htop -d 10 # 设置刷新间隔 +``` + +### kill - 终止进程 + +**基本语法:** `kill [选项] PID` + +**常用信号:** +- `-9` SIGKILL(强制终止) +- `-15` SIGTERM(正常终止,默认) +- `-1` SIGHUP(重新加载配置) +- `-2` SIGINT(中断,Ctrl+C) + +**实例:** +```bash +# 查找要终止的进程 +ps aux | grep firefox +# 输出:user 12345 2.1 5.4 2547696 437184 ? Sl 10:30 1:23 firefox + +# 正常终止进程 +kill 12345 + +# 强制终止进程 +kill -9 12345 + +# 按进程名终止 +killall firefox + +# 终止多个进程 +kill 12345 67890 13579 + +# 发送特定信号 +kill -HUP 12345 # 重新加载配置 + +# 显示所有可用信号 +kill -l +# 输出: +# 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL +# 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE +# 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 +``` + +### jobs - 作业控制 + +**基本语法:** `jobs [选项]` + +**相关命令:** +- `bg` 将作业放到后台 +- `fg` 将作业调到前台 +- `nohup` 不挂断运行 + +**实例:** +```bash +# 启动后台作业 +sleep 300 & +# 输出:[1] 12345 + +# 查看作业状态 +jobs +# 输出:[1]+ Running sleep 300 & + +# 暂停当前作业(Ctrl+Z) +vim large_file.txt +# 按Ctrl+Z暂停 +# 输出:[2]+ Stopped vim large_file.txt + +# 查看所有作业 +jobs +# 输出: +# [1]- Running sleep 300 & +# [2]+ Stopped vim large_file.txt + +# 将作业调到前台 +fg %2 +# vim回到前台 + +# 将停止的作业放到后台运行 +bg %2 + +# 使用nohup运行不挂断命令 +nohup long_running_script.sh & +# 输出:nohup: ignoring input and appending output to 'nohup.out' +``` + +--- + +## 网络命令 + +### ping - 测试网络连通性 + +**基本语法:** `ping [选项] 目标主机` + +**常用选项:** +- `-c` 指定ping次数 +- `-i` 指定间隔时间 +- `-s` 指定数据包大小 +- `-t` 设置TTL值 + +**实例:** +```bash +# 基本ping测试 +ping google.com +# 输出: +# PING google.com (172.217.160.142) 56(84) bytes of data. +# 64 bytes from lga25s62-in-f14.1e100.net (172.217.160.142): icmp_seq=1 ttl=117 time=12.3 ms +# 64 bytes from lga25s62-in-f14.1e100.net (172.217.160.142): icmp_seq=2 ttl=117 time=11.8 ms + +# 指定ping次数 +ping -c 3 google.com +# 输出:发送3个包后停止 + +# 设置包大小 +ping -s 1000 google.com +# 输出:发送1000字节的数据包 + +# 设置间隔时间 +ping -i 2 google.com +# 输出:每2秒发送一个包 + +# ping本地回环 +ping localhost +# 或 +ping 127.0.0.1 +# 输出: +# PING localhost (127.0.0.1) 56(84) bytes of data. +# 64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.045 ms + +# 统计信息 +ping -c 5 google.com +# 最终输出统计: +# --- google.com ping statistics --- +# 5 packets transmitted, 5 received, 0% packet loss, time 4006ms +# rtt min/avg/max/mdev = 11.234/12.567/13.890/0.892 ms +``` + +### wget - 下载文件 + +**基本语法:** `wget [选项] URL` + +**常用选项:** +- `-O` 指定输出文件名 +- `-c` 断点续传 +- `-r` 递归下载 +- `-P` 指定下载目录 +- `--limit-rate` 限制下载速度 + +**实例:** +```bash +# 基本下载 +wget https://example.com/file.zip +# 输出: +# --2023-12-24 14:30:25-- https://example.com/file.zip +# Resolving example.com (example.com)... 93.184.216.34 +# Connecting to example.com (example.com)|93.184.216.34|:443... connected. +# HTTP request sent, awaiting response... 200 OK +# Length: 1048576 (1.0M) [application/zip] +# Saving to: 'file.zip' +# file.zip 100%[==============>] 1.00M 2.34MB/s in 0.4s + +# 指定输出文件名 +wget -O myfile.zip https://example.com/file.zip + +# 断点续传 +wget -c https://example.com/largefile.iso + +# 后台下载 +wget -b https://example.com/largefile.iso +# 输出:Continuing in background, pid 12345. + +# 限制下载速度 +wget --limit-rate=200k https://example.com/file.zip + +# 下载整个网站(谨慎使用) +wget -r -np -k https://example.com/ + +# 设置用户代理 +wget --user-agent="Mozilla/5.0" https://example.com/file.zip + +# 设置重试次数 +wget --tries=3 https://example.com/file.zip +``` + +### curl - 数据传输工具 + +**基本语法:** `curl [选项] URL` + +**常用选项:** +- `-o` 输出到文件 +- `-O` 使用远程文件名 +- `-L` 跟随重定向 +- `-H` 添加请求头 +- `-d` 发送POST数据 + +**实例:** +```bash +# 基本GET请求 +curl https://api.github.com/users/octocat +# 输出:JSON格式的用户信息 + +# 保存到文件 +curl -o user.json https://api.github.com/users/octocat + +# 使用原文件名保存 +curl -O https://example.com/file.zip + +# 跟随重定向 +curl -L https://github.com + +# 显示详细信息 +curl -v https://httpbin.org/get +# 输出: +# * Trying 54.175.219.8:443... +# * Connected to httpbin.org (54.175.219.8) port 443 (#0) +# * ALPN, offering h2 +# * ALPN, offering http/1.1 +# > GET /get HTTP/1.1 +# > Host: httpbin.org +# > User-Agent: curl/7.68.0 +# > Accept: */* + +# POST请求发送JSON数据 +curl -X POST -H "Content-Type: application/json" \ + -d '{"name":"John","age":30}' \ + https://httpbin.org/post + +# 上传文件 +curl -X POST -F "file=@/path/to/file.txt" https://httpbin.org/post + +# 设置请求头 +curl -H "Authorization: Bearer token123" \ + -H "Accept: application/json" \ + https://api.example.com/data + +# 测试网站响应时间 +curl -w "Total time: %{time_total}s\n" -o /dev/null -s https://google.com +# 输出:Total time: 0.123456s +``` + +### netstat - 显示网络连接 + +**基本语法:** `netstat [选项]` + +**常用选项:** +- `-a` 显示所有连接 +- `-t` 显示TCP连接 +- `-u` 显示UDP连接 +- `-l` 只显示监听端口 +- `-n` 显示数字地址 +- `-p` 显示进程信息 + +**实例:** +```bash +# 显示所有网络连接 +netstat -a | head -10 +# 输出: +# Active Internet connections (servers and established) +# Proto Recv-Q Send-Q Local Address Foreign Address State +# tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN +# tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN + +# 显示监听端口 +netstat -tlnp +# 输出: +# Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name +# tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1234/sshd +# tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 5678/mysqld + +# 显示TCP连接状态统计 +netstat -st +# 输出: +# Tcp: +# 12345 active connections openings +# 67890 passive connection openings +# 123 failed connection attempts + +# 查看特定端口 +netstat -tlnp | grep :80 +# 输出:tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 9876/apache2 + +# 显示路由表 +netstat -rn +# 输出: +# Kernel IP routing table +# Destination Gateway Genmask Flags MSS Window irtt Iface +# 0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0 +# 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +``` + +### ss - Socket Statistics + +**基本语法:** `ss [选项]` + +**常用选项:** +- `-a` 显示所有socket +- `-t` 显示TCP socket +- `-u` 显示UDP socket +- `-l` 显示监听状态 +- `-n` 不解析服务名 +- `-p` 显示进程信息 + +**实例:** +```bash +# 显示所有TCP连接 +ss -t +# 输出: +# State Recv-Q Send-Q Local Address:Port Peer Address:Port +# ESTAB 0 0 192.168.1.100:22 192.168.1.10:12345 + +# 显示监听端口 +ss -tlnp +# 输出: +# State Recv-Q Send-Q Local Address:Port Peer Address:Port Process +# LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3)) +# LISTEN 0 80 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=5678,fd=10)) + +# 显示特定端口的连接 +ss -tlnp sport = :80 +# 或 +ss -tlnp | grep :80 + +# 显示连接统计 +ss -s +# 输出: +# Total: 234 (kernel 456) +# TCP: 12 (estab 3, closed 5, orphaned 0, synrecv 0, timewait 4/0), ports 0 +# Transport Total IP IPv6 +# * 456 - - +# RAW 0 0 0 +# UDP 8 5 3 +# TCP 7 4 3 + +# 按状态过滤 +ss -t state established +ss -t state listening +``` + +--- + +## 权限和用户管理 + +### chmod - 修改文件权限 + +**基本语法:** `chmod [选项] 权限 文件名` + +**权限表示方法:** +- 数字方式:读(4) + 写(2) + 执行(1) +- 字符方式:r(读) w(写) x(执行) +- 用户类别:u(所有者) g(组) o(其他) a(所有) + +**实例:** +```bash +# 创建测试文件 +echo "Hello World" > test.txt +ls -l test.txt +# 输出:-rw-r--r-- 1 user user 12 Dec 24 15:00 test.txt + +# 数字方式设置权限 +chmod 755 test.txt +ls -l test.txt +# 输出:-rwxr-xr-x 1 user user 12 Dec 24 15:00 test.txt + +# 字符方式添加执行权限 +chmod +x test.txt +ls -l test.txt +# 输出:-rwxr-xr-x 1 user user 12 Dec 24 15:00 test.txt + +# 为所有者添加写权限 +chmod u+w test.txt + +# 为组和其他用户移除写权限 +chmod go-w test.txt + +# 设置精确权限 +chmod u=rwx,g=rx,o=r test.txt +ls -l test.txt +# 输出:-rwxr-xr-- 1 user user 12 Dec 24 15:00 test.txt + +# 递归修改目录权限 +mkdir -p test_dir/subdir +chmod -R 755 test_dir/ +ls -la test_dir/ +# 输出: +# total 8 +# drwxr-xr-x 3 user user 4096 Dec 24 15:01 . +# drwxr-xr-x 3 user user 4096 Dec 24 15:01 .. +# drwxr-xr-x 2 user user 4096 Dec 24 15:01 subdir + +# 常用权限组合 +chmod 644 document.txt # 文件:所有者读写,其他只读 +chmod 755 script.sh # 脚本:所有者全权限,其他读执行 +chmod 700 private_dir/ # 目录:只有所有者可访问 +chmod 666 shared.txt # 文件:所有人读写 +``` + +### chown - 修改文件所有者 + +**基本语法:** `chown [选项] 所有者[:组] 文件名` + +**常用选项:** +- `-R` 递归修改 +- `-v` 显示详细过程 +- `--reference` 参考其他文件 + +**实例:** +```bash +# 创建测试文件 +echo "test" > ownership_test.txt +ls -l ownership_test.txt +# 输出:-rw-r--r-- 1 user user 5 Dec 24 15:05 ownership_test.txt + +# 修改所有者(需要sudo权限) +sudo chown root ownership_test.txt +ls -l ownership_test.txt +# 输出:-rw-r--r-- 1 root user 5 Dec 24 15:05 ownership_test.txt + +# 同时修改所有者和组 +sudo chown root:root ownership_test.txt +ls -l ownership_test.txt +# 输出:-rw-r--r-- 1 root root 5 Dec 24 15:05 ownership_test.txt + +# 只修改组 +sudo chown :user ownership_test.txt +ls -l ownership_test.txt +# 输出:-rw-r--r-- 1 root user 5 Dec 24 15:05 ownership_test.txt + +# 递归修改目录 +sudo mkdir -p /tmp/test_ownership/subdir +sudo chown -R user:user /tmp/test_ownership/ +ls -la /tmp/test_ownership/ +# 输出:所有文件和子目录的所有者都变为user + +# 参考其他文件的所有权 +sudo chown --reference=/home/user/reference.txt target.txt + +# 显示详细过程 +sudo chown -v user:group file.txt +# 输出:changed ownership of 'file.txt' from root:root to user:group +``` + +### su - 切换用户 + +**基本语法:** `su [选项] [用户名]` + +**常用选项:** +- `-` 或 `-l` 完全切换用户环境 +- `-c` 执行单个命令 +- `-s` 指定shell + +**实例:** +```bash +# 切换到root用户 +su - +# 输入root密码后: +# root@hostname:~# + +# 切换到指定用户 +su username +# 输入username的密码 + +# 完全切换用户环境 +su - username +# 这会加载用户的环境变量和工作目录 + +# 以其他用户身份执行命令 +su -c "ls -la /root/" root +# 输入root密码后执行ls命令 + +# 查看当前用户 +whoami +# 输出:current_username + +# 查看用户ID信息 +id +# 输出:uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo) + +# 查看登录用户 +who +# 输出: +# user pts/0 2023-12-24 14:30 (192.168.1.10) +# admin pts/1 2023-12-24 15:00 (192.168.1.20) + +# 查看系统登录历史 +last | head -5 +# 输出: +# user pts/0 192.168.1.10 Sun Dec 24 14:30 still logged in +# user pts/0 192.168.1.10 Sun Dec 24 10:15 - 14:25 (04:10) +``` + +### sudo - 以超级用户执行命令 + +**基本语法:** `sudo [选项] 命令` + +**常用选项:** +- `-u` 指定用户 +- `-i` 模拟登录 +- `-s` 启动shell +- `-l` 列出权限 + +**实例:** +```bash +# 基本使用 +sudo ls /root/ +# 输入当前用户密码(不是root密码) + +# 以指定用户执行命令 +sudo -u www-data ls /var/www/ + +# 启动root shell +sudo -i +# 或 +sudo -s + +# 列出当前用户的sudo权限 +sudo -l +# 输出: +# User user may run the following commands on hostname: +# (ALL : ALL) ALL + +# 编辑sudo配置(危险操作) +sudo visudo + +# 不输入密码执行命令(需要配置NOPASSWD) +# 在/etc/sudoers中添加:user ALL=(ALL) NOPASSWD: /bin/ls +sudo ls /root/ # 不需要密码 + +# 以root身份编辑文件 +sudo vim /etc/hosts + +# 切换到root环境 +sudo su - + +# 查看sudo日志 +sudo tail /var/log/auth.log | grep sudo +# 输出:sudo命令的执行记录 +``` + +--- + +## 压缩和归档 + +### tar - 归档文件 + +**基本语法:** `tar [选项] 归档文件 文件列表` + +**常用选项:** +- `-c` 创建归档 +- `-x` 解压归档 +- `-t` 列出内容 +- `-v` 显示详细过程 +- `-f` 指定归档文件名 +- `-z` gzip压缩 +- `-j` bzip2压缩 + +**实例:** +```bash +# 创建测试文件和目录 +mkdir -p test_archive/dir1/dir2 +echo "File 1 content" > test_archive/file1.txt +echo "File 2 content" > test_archive/dir1/file2.txt +echo "File 3 content" > test_archive/dir1/dir2/file3.txt + +# 创建tar归档 +tar -cvf archive.tar test_archive/ +# 输出: +# test_archive/ +# test_archive/file1.txt +# test_archive/dir1/ +# test_archive/dir1/file2.txt +# test_archive/dir1/dir2/ +# test_archive/dir1/dir2/file3.txt + +# 查看归档内容 +tar -tvf archive.tar +# 输出: +# drwxr-xr-x user/user 0 2023-12-24 15:10 test_archive/ +# -rw-r--r-- user/user 15 2023-12-24 15:10 test_archive/file1.txt + +# 解压归档 +mkdir extract_test +cd extract_test +tar -xvf ../archive.tar + +# 创建gzip压缩归档 +tar -czvf archive.tar.gz test_archive/ +# 输出:归档并压缩过程 + +# 解压gzip归档 +tar -xzvf archive.tar.gz + +# 创建bzip2压缩归档 +tar -cjvf archive.tar.bz2 test_archive/ + +# 只提取特定文件 +tar -xvf archive.tar test_archive/file1.txt + +# 追加文件到已存在的归档 +echo "New file" > newfile.txt +tar -rvf archive.tar newfile.txt + +# 查看压缩比 +ls -lh archive.tar* +# 输出: +# -rw-r--r-- 1 user user 10K Dec 24 15:15 archive.tar +# -rw-r--r-- 1 user user 2.1K Dec 24 15:16 archive.tar.gz +# -rw-r--r-- 1 user user 2.0K Dec 24 15:17 archive.tar.bz2 +``` + +### gzip/gunzip - 压缩/解压文件 + +**基本语法:** `gzip [选项] 文件名` + +**常用选项:** +- `-d` 解压(等同于gunzip) +- `-r` 递归压缩目录 +- `-t` 测试压缩文件 +- `-v` 显示详细信息 +- `-1到-9` 压缩级别 + +**实例:** +```bash +# 创建测试文件 +echo "This is a test file for compression demonstration. It contains some text that can be compressed to show the effectiveness of gzip compression." > test_compression.txt +ls -l test_compression.txt +# 输出:-rw-r--r-- 1 user user 138 Dec 24 15:20 test_compression.txt + +# 基本压缩 +gzip test_compression.txt +ls -l test_compression.txt.gz +# 输出:-rw-r--r-- 1 user user 95 Dec 24 15:20 test_compression.txt.gz +# 注意:原文件已被替换为压缩文件 + +# 保留原文件进行压缩 +cp test_compression.txt.gz temp.gz +gunzip temp.gz +gzip -c test_compression.txt > test_compression_copy.txt.gz +ls -l test_compression* +# 输出:显示原文件和压缩文件都存在 + +# 解压文件 +gunzip test_compression.txt.gz +# 或 +gzip -d test_compression_copy.txt.gz + +# 不同压缩级别对比 +echo "Testing different compression levels" > compression_test.txt +cp compression_test.txt test1.txt +cp compression_test.txt test9.txt + +gzip -1 test1.txt # 最快压缩 +gzip -9 test9.txt # 最大压缩 + +ls -l test*.txt.gz +# 输出:比较不同压缩级别的文件大小 + +# 测试压缩文件完整性 +gzip -t test1.txt.gz +echo $? +# 输出:0(成功) + +# 显示压缩信息 +gzip -l test1.txt.gz +# 输出: +# compressed uncompressed ratio uncompressed_name +# 45 34 -8.8% test1.txt + +# 递归压缩目录中的文件 +mkdir -p compress_dir +echo "file1" > compress_dir/file1.txt +echo "file2" > compress_dir/file2.txt +gzip -r compress_dir/ +ls -la compress_dir/ +# 输出:目录中的所有文件都被压缩 +``` + +### zip/unzip - 创建和解压ZIP文件 + +**基本语法:** `zip [选项] 压缩文件.zip 文件列表` + +**常用选项:** +- `-r` 递归压缩目录 +- `-v` 显示详细过程 +- `-u` 更新压缩文件 +- `-d` 从压缩文件中删除 +- `-x` 排除特定文件 + +**实例:** +```bash +# 创建测试文件结构 +mkdir -p zip_test/{docs,images,scripts} +echo "Document 1" > zip_test/docs/doc1.txt +echo "Document 2" > zip_test/docs/doc2.txt +echo "Image placeholder" > zip_test/images/image1.jpg +echo "#!/bin/bash\necho 'Hello World'" > zip_test/scripts/script1.sh + +# 创建ZIP文件 +zip -r project.zip zip_test/ +# 输出: +# adding: zip_test/ (stored 0%) +# adding: zip_test/docs/ (stored 0%) +# adding: zip_test/docs/doc1.txt (stored 0%) +# adding: zip_test/docs/doc2.txt (stored 0%) + +# 查看ZIP文件内容 +unzip -l project.zip +# 输出: +# Archive: project.zip +# Length Date Time Name +# --------- ---------- ----- ---- +# 0 12-24-2023 15:25 zip_test/ +# 0 12-24-2023 15:25 zip_test/docs/ +# 12 12-24-2023 15:25 zip_test/docs/doc1.txt + +# 解压ZIP文件 +mkdir extract_zip +cd extract_zip +unzip ../project.zip +# 输出:解压过程详情 + +# 只解压特定文件 +unzip project.zip zip_test/docs/doc1.txt + +# 解压时不覆盖已存在文件 +unzip -n project.zip + +# 静默解压(不显示输出) +unzip -q project.zip + +# 测试ZIP文件完整性 +unzip -t project.zip +# 输出:testing: zip_test/docs/doc1.txt OK + +# 排除特定文件类型 +zip -r project_no_images.zip zip_test/ -x "*.jpg" + +# 添加文件到现有ZIP +echo "New document" > newdoc.txt +zip project.zip newdoc.txt + +# 从ZIP中删除文件 +zip -d project.zip newdoc.txt + +# 更新ZIP中的文件 +echo "Updated document" > zip_test/docs/doc1.txt +zip -u project.zip zip_test/docs/doc1.txt + +# 创建密码保护的ZIP文件 +zip -r -P mypassword secure.zip zip_test/ +# 解压时需要密码: +# unzip secure.zip +``` + +--- + +## 查找和搜索 + +### find - 查找文件和目录 + +**基本语法:** `find [路径] [条件] [动作]` + +**常用条件:** +- `-name` 按名称查找 +- `-type` 按类型查找 +- `-size` 按大小查找 +- `-mtime` 按修改时间查找 +- `-perm` 按权限查找 + +**实例:** +```bash +# 创建测试文件结构 +mkdir -p find_test/{level1/{level2,level3},old_files} +echo "content" > find_test/test.txt +echo "content" > find_test/level1/data.log +echo "content" > find_test/level1/level2/config.ini +echo "old content" > find_test/old_files/archive.bak +chmod 755 find_test/test.txt +chmod 644 find_test/level1/data.log + +# 按名称查找文件 +find find_test/ -name "*.txt" +# 输出:find_test/test.txt + +# 按名称查找(不区分大小写) +find find_test/ -iname "*.LOG" +# 输出:find_test/level1/data.log + +# 按类型查找 +find find_test/ -type f # 查找文件 +# 输出:所有文件 + +find find_test/ -type d # 查找目录 +# 输出:所有目录 + +# 按大小查找 +find find_test/ -size +0c # 大于0字节的文件 +find find_test/ -size -1k # 小于1KB的文件 + +# 按权限查找 +find find_test/ -perm 755 +# 输出:find_test/test.txt + +# 按修改时间查找 +find find_test/ -mtime -1 # 最近1天修改的文件 +find find_test/ -mtime +7 # 7天前修改的文件 + +# 组合条件查找 +find find_test/ -name "*.txt" -o -name "*.log" +# 输出:查找.txt或.log文件 + +find find_test/ -type f -name "*.txt" -size +0c +# 输出:同时满足多个条件的文件 + +# 执行动作 +find find_test/ -name "*.bak" -delete # 删除.bak文件 + +find find_test/ -type f -exec ls -l {} \; +# 输出:对找到的每个文件执行ls -l + +find find_test/ -name "*.log" -exec grep "content" {} \; +# 输出:在找到的.log文件中搜索"content" + +# 查找并统计 +find find_test/ -type f | wc -l +# 输出:文件总数 + +# 查找最大的文件 +find find_test/ -type f -exec ls -s {} \; | sort -n -r | head -5 +# 输出:前5个最大的文件 + +# 查找空文件和空目录 +find find_test/ -empty +# 输出:空文件和空目录 + +# 按用户查找 +find /home -user $USER -name "*.txt" 2>/dev/null +# 输出:当前用户的.txt文件 + +# 查找并复制 +find find_test/ -name "*.txt" -exec cp {} /tmp/ \; +# 将找到的.txt文件复制到/tmp/ +``` + +### locate - 快速查找文件 + +**基本语法:** `locate [选项] 模式` + +**常用选项:** +- `-i` 忽略大小写 +- `-l` 限制输出数量 +- `-r` 使用正则表达式 +- `-c` 只显示计数 + +**实例:** +```bash +# 更新locate数据库(需要sudo权限) +sudo updatedb + +# 基本查找 +locate test.txt +# 输出:系统中所有名为test.txt的文件路径 + +# 忽略大小写查找 +locate -i TEST.TXT +# 输出:不区分大小写的结果 + +# 限制结果数量 +locate -l 5 "*.log" +# 输出:最多5个.log文件 + +# 只显示计数 +locate -c "*.txt" +# 输出:系统中.txt文件的总数,如:1234 + +# 使用正则表达式 +locate -r "test.*\.txt$" +# 输出:匹配正则表达式的文件 + +# 查找目录 +locate -d bin +# 输出:包含bin的目录路径 + +# 查找特定路径下的文件 +locate "/home/user/*.txt" +# 输出:用户主目录下的.txt文件 + +# 组合使用 +locate "*.log" | grep apache +# 输出:包含apache的.log文件 + +# 查看locate数据库信息 +locate -S +# 输出: +# Database /var/lib/mlocate/mlocate.db: +# 21,100 directories +# 136,371 files +# 7,251,611 bytes in file names +# 3,264,341 bytes used to store database +``` + +### which - 查找可执行文件路径 + +**基本语法:** `which [选项] 命令名` + +**常用选项:** +- `-a` 显示所有匹配项 + +**实例:** +```bash +# 查找命令位置 +which ls +# 输出:/bin/ls + +which python3 +# 输出:/usr/bin/python3 + +which git +# 输出:/usr/bin/git + +# 显示所有匹配项 +which -a python +# 输出: +# /usr/bin/python +# /usr/local/bin/python + +# 查找多个命令 +which ls grep awk +# 输出: +# /bin/ls +# /bin/grep +# /usr/bin/awk + +# 检查命令是否存在 +which nonexistent_command +echo $? +# 输出:1(表示未找到) + +which ls +echo $? +# 输出:0(表示找到) + +# 在脚本中使用 +if which docker >/dev/null 2>&1; then + echo "Docker is installed" +else + echo "Docker is not installed" +fi +``` + +### whereis - 查找命令、源码和手册页 + +**基本语法:** `whereis [选项] 文件名` + +**常用选项:** +- `-b` 只查找可执行文件 +- `-m` 只查找手册页 +- `-s` 只查找源码 + +**实例:** +```bash +# 查找命令的所有相关文件 +whereis ls +# 输出:ls: /bin/ls /usr/share/man/man1/ls.1.gz + +# 只查找可执行文件 +whereis -b ls +# 输出:ls: /bin/ls + +# 只查找手册页 +whereis -m ls +# 输出:ls: /usr/share/man/man1/ls.1.gz + +# 查找多个命令 +whereis python3 pip3 +# 输出: +# python3: /usr/bin/python3 /usr/lib/python3 /etc/python3 /usr/share/man/man1/python3.1.gz +# pip3: /usr/bin/pip3 + +# 查找系统工具 +whereis gcc +# 输出:gcc: /usr/bin/gcc /usr/lib/gcc /usr/share/man/man1/gcc.1.gz + +whereis make +# 输出:make: /usr/bin/make /usr/share/man/man1/make.1.gz +``` + +--- + +## 系统监控 + +### df - 显示磁盘空间使用情况 + +**基本语法:** `df [选项] [文件系统]` + +**常用选项:** +- `-h` 人类可读格式 +- `-T` 显示文件系统类型 +- `-i` 显示inode使用情况 +- `-a` 显示所有文件系统 + +**实例:** +```bash +# 基本磁盘使用情况 +df +# 输出: +# Filesystem 1K-blocks Used Available Use% Mounted on +# /dev/sda1 20971520 8388608 11534336 43% / +# /dev/sda2 10485760 2097152 7864320 22% /home + +# 人类可读格式 +df -h +# 输出: +# Filesystem Size Used Avail Use% Mounted on +# /dev/sda1 20G 8.0G 11G 43% / +# /dev/sda2 10G 2.0G 7.5G 22% /home +# tmpfs 2.0G 0 2.0G 0% /dev/shm + +# 显示文件系统类型 +df -hT +# 输出: +# Filesystem Type Size Used Avail Use% Mounted on +# /dev/sda1 ext4 20G 8.0G 11G 43% / +# /dev/sda2 ext4 10G 2.0G 7.5G 22% /home +# tmpfs tmpfs 2.0G 0 2.0G 0% /dev/shm + +# 显示inode使用情况 +df -i +# 输出: +# Filesystem Inodes IUsed IFree IUse% Mounted on +# /dev/sda1 1310720 131072 1179648 10% / +# /dev/sda2 655360 65536 589824 10% /home + +# 显示特定目录的磁盘使用 +df -h /home +# 输出:/home目录所在文件系统的使用情况 + +# 显示所有文件系统 +df -a +# 输出:包括虚拟文件系统在内的所有挂载点 + +# 按使用率排序 +df -h | sort -k5 -r +# 输出:按使用率从高到低排序的结果 +``` + +### du - 显示目录空间使用情况 + +**基本语法:** `du [选项] [目录]` + +**常用选项:** +- `-h` 人类可读格式 +- `-s` 只显示总计 +- `-a` 显示所有文件 +- `-d` 指定深度 +- `--max-depth` 最大深度 + +**实例:** +```bash +# 创建测试目录结构 +mkdir -p du_test/{dir1,dir2,dir3} +echo "Small file content" > du_test/small.txt +dd if=/dev/zero of=du_test/dir1/large.dat bs=1M count=10 2>/dev/null +echo "Another file" > du_test/dir2/file.txt +dd if=/dev/zero of=du_test/dir3/medium.dat bs=1K count=500 2>/dev/null + +# 显示当前目录使用情况 +du -h du_test/ +# 输出: +# 500K du_test/dir3 +# 19M du_test/dir1 +# 4.0K du_test/dir2 +# 19M du_test + +# 只显示总计 +du -sh du_test/ +# 输出:19M du_test + +# 显示所有文件和目录 +du -ah du_test/ +# 输出: +# 4.0K du_test/small.txt +# 10M du_test/dir1/large.dat +# 10M du_test/dir1 +# 4.0K du_test/dir2/file.txt + +# 限制显示深度 +du -h --max-depth=1 du_test/ +# 输出:只显示第一层目录 + +# 按大小排序 +du -ah du_test/ | sort -hr +# 输出: +# 19M du_test +# 10M du_test/dir1/large.dat +# 10M du_test/dir1 +# 500K du_test/dir3/medium.dat +# 500K du_test/dir3 + +# 查找最大的目录 +du -h du_test/*/ | sort -hr | head -3 +# 输出:前3个最大的子目录 + +# 显示当前目录下所有子目录大小 +du -sh */ 2>/dev/null | sort -hr +# 输出:当前目录下所有子目录按大小排序 + +# 查找大文件 +find du_test/ -type f -exec du -h {} \; | sort -hr | head -5 +# 输出:前5个最大的文件 + +# 排除特定文件类型 +du -ah du_test/ --exclude="*.dat" +# 输出:排除.dat文件的大小统计 +``` + +### free - 显示内存使用情况 + +**基本语法:** `free [选项]` + +**常用选项:** +- `-h` 人类可读格式 +- `-m` 以MB显示 +- `-g` 以GB显示 +- `-s` 持续监控 +- `-t` 显示总计 + +**实例:** +```bash +# 基本内存信息 +free +# 输出: +# total used free shared buff/cache available +# Mem: 8165536 2048000 3584000 512000 2533536 5324000 +# Swap: 2097152 0 2097152 + +# 人类可读格式 +free -h +# 输出: +# total used free shared buff/cache available +# Mem: 7.8G 2.0G 3.4G 500M 2.4G 5.1G +# Swap: 2.0G 0B 2.0G + +# 以MB显示 +free -m +# 输出: +# total used free shared buff/cache available +# Mem: 7972 2000 3500 500 2472 5200 +# Swap: 2048 0 2048 + +# 显示总计 +free -ht +# 输出: +# total used free shared buff/cache available +# Mem: 7.8G 2.0G 3.4G 500M 2.4G 5.1G +# Swap: 2.0G 0B 2.0G +# Total: 9.8G 2.0G 5.4G + +# 持续监控(每2秒更新) +free -h -s 2 +# 输出:每2秒更新一次的内存使用情况 + +# 只显示一次更新 +free -h -c 1 +# 输出:显示一次后退出 + +# 计算内存使用率 +free | awk 'NR==2{printf "Memory Usage: %.2f%%\n", $3*100/$2}' +# 输出:Memory Usage: 25.12% + +# 监控内存变化 +watch -n 1 'free -h' +# 输出:每秒更新一次的内存监控界面 +``` + +### iostat - I/O统计信息 + +**基本语法:** `iostat [选项] [间隔] [次数]` + +**常用选项:** +- `-x` 显示扩展统计 +- `-d` 只显示磁盘统计 +- `-c` 只显示CPU统计 +- `-h` 人类可读格式 + +**实例:** +```bash +# 安装sysstat包(如果未安装) +# sudo apt install sysstat # Ubuntu/Debian +# sudo yum install sysstat # CentOS/RHEL + +# 基本I/O统计 +iostat +# 输出: +# Linux 5.4.0 (hostname) 12/24/2023 _x86_64_ (4 CPU) +# +# avg-cpu: %user %nice %system %iowait %steal %idle +# 2.34 0.00 1.23 0.45 0.00 95.98 +# +# Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn +# sda 12.34 123.45 67.89 1234567 678901 + +# 显示扩展统计信息 +iostat -x +# 输出: +# Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util +# sda 5.67 6.78 123.45 67.89 0.12 1.34 2.07 16.50 2.34 4.56 0.03 21.78 10.01 1.23 0.85 + +# 只显示磁盘统计 +iostat -d 2 3 +# 输出:每2秒更新一次,共3次 + +# 显示特定设备 +iostat -x sda 1 +# 输出:每秒更新sda设备的详细统计 + +# 人类可读格式 +iostat -h +# 输出:以更易读的格式显示大小 + +# 持续监控高I/O负载 +iostat -x 1 | grep -E "Device|sda" +# 输出:持续监控sda设备的I/O情况 + +# 分析I/O等待时间 +iostat -x 1 | awk '/sda/ && $10 > 10 {print strftime("%Y-%m-%d %H:%M:%S"), "High await:", $10}' +# 输出:当I/O等待时间超过10ms时的警告 +``` + +### vmstat - 虚拟内存统计 + +**基本语法:** `vmstat [选项] [间隔] [次数]` + +**常用选项:** +- `-a` 显示活跃和非活跃内存 +- `-d` 显示磁盘统计 +- `-s` 显示内存统计摘要 +- `-t` 显示时间戳 + +**实例:** +```bash +# 基本虚拟内存统计 +vmstat +# 输出: +# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- +# r b swpd free buff cache si so bi bo in cs us sy id wa st +# 1 0 0 3584000 123456 2345678 0 0 10 20 100 200 2 1 97 0 0 + +# 每2秒更新一次,共5次 +vmstat 2 5 +# 输出:显示5次统计,每次间隔2秒 + +# 显示活跃和非活跃内存 +vmstat -a +# 输出: +# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- +# r b swpd free inact active si so bi bo in cs us sy id wa st +# 0 0 0 3584000 1234567 5678901 0 0 10 20 100 200 2 1 97 0 0 + +# 显示磁盘统计 +vmstat -d +# 输出: +# disk- ------------reads------------ ------------writes----------- -----IO------ +# total merged sectors ms total merged sectors ms cur sec +# sda 12345 123 1234567 12345 67890 678 678901 6789 0 12 + +# 显示内存统计摘要 +vmstat -s +# 输出: +# 8165536 K total memory +# 2048000 K used memory +# 5678901 K active memory +# 1234567 K inactive memory + +# 显示时间戳 +vmstat -t 1 +# 输出:每行都带有时间戳的统计信息 + +# 监控系统负载 +vmstat 1 | awk '{if(NR>2 && ($1+$2)>2) print strftime("%H:%M:%S"), "High load:", $1+$2}' +# 输出:当系统负载过高时的警告 + +# 监控内存使用 +vmstat 1 | awk 'NR>2 {if($4<1000000) print strftime("%H:%M:%S"), "Low memory:", $4"K"}' +# 输出:当可用内存低于1GB时的警告 +``` + +### sar - 系统活动报告 + +**基本语法:** `sar [选项] [间隔] [次数]` + +**常用选项:** +- `-u` CPU使用率 +- `-r` 内存使用率 +- `-d` 磁盘活动 +- `-n` 网络统计 +- `-q` 队列长度和负载 + +**实例:** +```bash +# CPU使用率统计 +sar -u 1 3 +# 输出: +# 15:30:01 CPU %user %nice %system %iowait %steal %idle +# 15:30:02 all 2.50 0.00 1.25 0.25 0.00 96.00 +# 15:30:03 all 3.00 0.00 1.50 0.00 0.00 95.50 + +# 内存使用率统计 +sar -r 1 3 +# 输出: +# 15:30:01 kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit +# 15:30:02 3584000 4581536 56.07 123456 2345678 2048000 25.06 + +# 磁盘活动统计 +sar -d 1 3 +# 输出: +# 15:30:01 DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util +# 15:30:02 sda 12.00 123.45 67.89 15.92 0.03 2.50 1.25 1.50 + +# 网络统计 +sar -n DEV 1 3 +# 输出: +# 15:30:01 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s +# 15:30:02 eth0 123.45 98.76 567.89 432.10 0.00 0.00 0.00 + +# 系统负载和队列长度 +sar -q 1 3 +# 输出: +# 15:30:01 runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15 blocked +# 15:30:02 1 234 0.15 0.10 0.05 0 + +# 查看历史数据(从日志文件) +sar -u -f /var/log/sysstat/sa$(date +%d) +# 输出:当天的CPU使用历史 + +# 生成报告 +sar -A -f /var/log/sysstat/sa$(date +%d) > system_report.txt +# 生成完整的系统活动报告 + +# 实时监控关键指标 +sar -u -r -d 2 | grep -E "Average|%idle|%memused|%util" +# 输出:关键性能指标的实时监控 +``` + +--- + +## 总结 + +本文档详细介绍了Linux系统中最常用的命令,包括: + +1. **文件操作**:ls, cd, mkdir, cp, mv, rm等基础文件管理命令 +2. **文本处理**:cat, grep, sed, awk, sort等强大的文本处理工具 +3. **系统管理**:ps, top, kill, jobs等进程和作业控制命令 +4. **网络工具**:ping, wget, curl, netstat等网络诊断和数据传输工具 +5. **权限管理**:chmod, chown, su, sudo等权限和用户管理命令 +6. **压缩归档**:tar, gzip, zip等文件压缩和归档工具 +7. **查找搜索**:find, locate, which, whereis等文件查找命令 +8. **系统监控**:df, du, free, iostat, vmstat, sar等系统性能监控工具 + +每个命令都提供了详细的语法说明、常用选项、实际示例和输出结果,便于学习和参考。这些命令是Linux系统管理和日常操作的基础,熟练掌握它们将大大提高工作效率。 + +建议读者: +- 在安全的环境中实际操作这些命令 +- 结合man手册页面深入学习每个命令的高级功能 +- 根据实际工作需要,重点掌握相关领域的命令 +- 定期练习,形成肌肉记忆 + +记住:实践是掌握Linux命令的最佳方式! \ No newline at end of file diff --git a/MySQL_GROUP_BY语法使用文档.md b/MySQL_GROUP_BY语法使用文档.md new file mode 100644 index 0000000..d41a3bc --- /dev/null +++ b/MySQL_GROUP_BY语法使用文档.md @@ -0,0 +1,954 @@ +# MySQL GROUP BY 语法使用文档 + +## 目录 +1. [GROUP BY 基础语法](#group-by-基础语法) +2. [GROUP BY 规则和特性](#group-by-规则和特性) +3. [示例数据准备](#示例数据准备) +4. [基础分组查询](#基础分组查询) +5. [聚合函数详解](#聚合函数详解) +6. [HAVING 子句使用](#having-子句使用) +7. [多列分组查询](#多列分组查询) +8. [复杂分组统计](#复杂分组统计) +9. [面试题和实际案例](#面试题和实际案例) +10. [性能优化和最佳实践](#性能优化和最佳实践) + +## GROUP BY 基础语法 + +GROUP BY 用于将查询结果按指定字段进行分组,通常与聚合函数一起使用。 + +```sql +SELECT column1, aggregate_function(column2) +FROM table_name +[WHERE condition] +GROUP BY column1, column2, ... +[HAVING group_condition] +[ORDER BY column] +[LIMIT number]; +``` + +**执行顺序:** +1. FROM - 确定数据源 +2. WHERE - 过滤原始数据 +3. GROUP BY - 分组 +4. 聚合函数计算 +5. HAVING - 过滤分组结果 +6. SELECT - 选择输出列 +7. ORDER BY - 排序 +8. LIMIT - 限制结果数量 + +## GROUP BY 规则和特性 + +1. **SELECT 列限制**:SELECT 子句中只能包含: + - GROUP BY 子句中的列 + - 聚合函数 + - 常量 + +2. **NULL 值处理**:NULL 值被视为一组 + +3. **聚合函数**: + - COUNT():计数 + - SUM():求和 + - AVG():平均值 + - MAX():最大值 + - MIN():最小值 + - GROUP_CONCAT():字符串连接 + +4. **HAVING vs WHERE**: + - WHERE:过滤原始行 + - HAVING:过滤分组结果 + +## 示例数据准备 + +### 创建示例表 + +```sql +-- 销售订单表 +CREATE TABLE sales_orders ( + order_id INT PRIMARY KEY AUTO_INCREMENT, + customer_id INT, + customer_name VARCHAR(50), + product_id INT, + product_name VARCHAR(100), + category VARCHAR(50), + order_date DATE, + quantity INT, + unit_price DECIMAL(10,2), + sales_rep VARCHAR(50), + region VARCHAR(30), + discount_rate DECIMAL(3,2) DEFAULT 0.00 +); + +-- 员工表 +CREATE TABLE employees ( + emp_id INT PRIMARY KEY, + name VARCHAR(50), + department VARCHAR(30), + position VARCHAR(50), + salary DECIMAL(10,2), + hire_date DATE, + manager_id INT, + age INT, + gender ENUM('M', 'F'), + city VARCHAR(30) +); + +-- 学生成绩表 +CREATE TABLE student_scores ( + student_id INT, + student_name VARCHAR(50), + subject VARCHAR(30), + score INT, + exam_date DATE, + teacher VARCHAR(30), + class_id INT, + semester VARCHAR(20), + PRIMARY KEY (student_id, subject, exam_date) +); + +-- 网站访问日志表 +CREATE TABLE web_logs ( + log_id INT PRIMARY KEY AUTO_INCREMENT, + user_id INT, + page_url VARCHAR(200), + visit_date DATE, + visit_time TIME, + session_duration INT, -- 会话时长(分钟) + device_type VARCHAR(20), + browser VARCHAR(30), + country VARCHAR(30), + page_views INT DEFAULT 1 +); + +-- 库存表 +CREATE TABLE inventory ( + product_id INT PRIMARY KEY, + product_name VARCHAR(100), + category VARCHAR(50), + supplier VARCHAR(50), + stock_quantity INT, + reorder_level INT, + unit_cost DECIMAL(8,2), + last_restock_date DATE, + warehouse_location VARCHAR(30) +); +``` + +### 插入示例数据 + +```sql +-- 插入销售订单数据 +INSERT INTO sales_orders (customer_id, customer_name, product_id, product_name, category, order_date, quantity, unit_price, sales_rep, region, discount_rate) VALUES +(1, '阿里巴巴', 101, 'MacBook Pro', '笔记本', '2024-01-15', 10, 12999.00, '张三', '华东', 0.05), +(1, '阿里巴巴', 102, 'iPhone 15', '手机', '2024-01-16', 50, 5999.00, '张三', '华东', 0.03), +(2, '腾讯', 103, 'iPad Air', '平板', '2024-01-20', 20, 3999.00, '李四', '华南', 0.04), +(2, '腾讯', 101, 'MacBook Pro', '笔记本', '2024-01-22', 15, 12999.00, '李四', '华南', 0.06), +(3, '百度', 104, 'AirPods Pro', '耳机', '2024-01-25', 100, 1999.00, '王五', '华北', 0.08), +(3, '百度', 102, 'iPhone 15', '手机', '2024-01-28', 30, 5999.00, '王五', '华北', 0.05), +(4, '字节跳动', 105, 'Apple Watch', '智能手表', '2024-02-01', 25, 2999.00, '赵六', '华北', 0.10), +(4, '字节跳动', 103, 'iPad Air', '平板', '2024-02-03', 35, 3999.00, '赵六', '华北', 0.07), +(5, '美团', 106, 'MacBook Air', '笔记本', '2024-02-05', 12, 8999.00, '钱七', '华东', 0.04), +(5, '美团', 104, 'AirPods Pro', '耳机', '2024-02-08', 80, 1999.00, '钱七', '华东', 0.06), +(6, '滴滴', 107, 'iMac', '台式机', '2024-02-10', 8, 15999.00, '孙八', '华南', 0.03), +(6, '滴滴', 102, 'iPhone 15', '手机', '2024-02-12', 40, 5999.00, '孙八', '华南', 0.04), +(7, '小米', 108, 'iPad Pro', '平板', '2024-02-15', 18, 7999.00, '周九', '华北', 0.05), +(7, '小米', 105, 'Apple Watch', '智能手表', '2024-02-18', 30, 2999.00, '周九', '华北', 0.12), +(8, '华为', 109, 'Studio Display', '显示器', '2024-02-20', 6, 11999.00, '吴十', '华南', 0.02); + +-- 插入员工数据 +INSERT INTO employees (emp_id, name, department, position, salary, hire_date, manager_id, age, gender, city) VALUES +(1001, '张经理', '技术部', '部门经理', 25000.00, '2020-01-15', NULL, 35, 'M', '北京'), +(1002, '李架构师', '技术部', '高级架构师', 22000.00, '2020-05-20', 1001, 32, 'M', '北京'), +(1003, '王工程师', '技术部', '高级工程师', 18000.00, '2021-03-10', 1001, 28, 'F', '北京'), +(1004, '赵工程师', '技术部', '中级工程师', 15000.00, '2022-01-15', 1001, 26, 'M', '北京'), +(1005, '钱实习生', '技术部', '实习工程师', 8000.00, '2023-07-01', 1002, 23, 'F', '北京'), +(2001, '孙经理', '产品部', '产品经理', 20000.00, '2021-02-01', NULL, 30, 'F', '上海'), +(2002, '周产品', '产品部', '高级产品', 16000.00, '2021-08-15', 2001, 27, 'M', '上海'), +(2003, '吴助理', '产品部', '产品助理', 12000.00, '2022-06-20', 2001, 25, 'F', '上海'), +(3001, '郑经理', '销售部', '销售经理', 18000.00, '2020-11-10', NULL, 33, 'M', '深圳'), +(3002, '刘销售', '销售部', '高级销售', 14000.00, '2021-09-25', 3001, 29, 'F', '深圳'), +(3003, '陈销售', '销售部', '销售代表', 10000.00, '2022-12-01', 3001, 24, 'M', '深圳'), +(4001, '林经理', '人事部', 'HR经理', 16000.00, '2021-04-12', NULL, 31, 'F', '广州'), +(4002, '黄专员', '人事部', 'HR专员', 11000.00, '2022-08-30', 4001, 26, 'F', '广州'), +(5001, '何经理', '财务部', '财务经理', 19000.00, '2020-07-08', NULL, 34, 'M', '杭州'), +(5002, '魏会计', '财务部', '会计', 13000.00, '2021-11-20', 5001, 28, 'F', '杭州'); + +-- 插入学生成绩数据 +INSERT INTO student_scores (student_id, student_name, subject, score, exam_date, teacher, class_id, semester) VALUES +(1, '张小明', '数学', 85, '2024-01-15', '王老师', 1, '2024春'), +(1, '张小明', '语文', 78, '2024-01-16', '李老师', 1, '2024春'), +(1, '张小明', '英语', 92, '2024-01-17', '赵老师', 1, '2024春'), +(2, '李小红', '数学', 92, '2024-01-15', '王老师', 1, '2024春'), +(2, '李小红', '语文', 88, '2024-01-16', '李老师', 1, '2024春'), +(2, '李小红', '英语', 95, '2024-01-17', '赵老师', 1, '2024春'), +(3, '王小刚', '数学', 76, '2024-01-15', '王老师', 2, '2024春'), +(3, '王小刚', '语文', 82, '2024-01-16', '李老师', 2, '2024春'), +(3, '王小刚', '英语', 79, '2024-01-17', '赵老师', 2, '2024春'), +(4, '赵小丽', '数学', 88, '2024-01-15', '王老师', 2, '2024春'), +(4, '赵小丽', '语文', 85, '2024-01-16', '李老师', 2, '2024春'), +(4, '赵小丽', '英语', 90, '2024-01-17', '赵老师', 2, '2024春'), +(5, '钱小伟', '数学', 90, '2024-01-15', '钱老师', 3, '2024春'), +(5, '钱小伟', '语文', 87, '2024-01-16', '孙老师', 3, '2024春'), +(5, '钱小伟', '英语', 93, '2024-01-17', '周老师', 3, '2024春'), +-- 添加期中考试成绩 +(1, '张小明', '数学', 88, '2024-03-15', '王老师', 1, '2024春'), +(1, '张小明', '语文', 82, '2024-03-16', '李老师', 1, '2024春'), +(2, '李小红', '数学', 95, '2024-03-15', '王老师', 1, '2024春'), +(2, '李小红', '语文', 90, '2024-03-16', '李老师', 1, '2024春'), +(3, '王小刚', '数学', 80, '2024-03-15', '王老师', 2, '2024春'); + +-- 插入网站访问日志数据 +INSERT INTO web_logs (user_id, page_url, visit_date, visit_time, session_duration, device_type, browser, country, page_views) VALUES +(1001, '/home', '2024-01-15', '09:30:00', 25, 'Desktop', 'Chrome', '中国', 5), +(1001, '/products', '2024-01-15', '10:15:00', 15, 'Desktop', 'Chrome', '中国', 8), +(1002, '/home', '2024-01-15', '14:20:00', 30, 'Mobile', 'Safari', '美国', 3), +(1003, '/about', '2024-01-16', '11:45:00', 20, 'Tablet', 'Chrome', '英国', 4), +(1001, '/checkout', '2024-01-16', '16:30:00', 45, 'Desktop', 'Chrome', '中国', 2), +(1004, '/home', '2024-01-17', '08:15:00', 35, 'Mobile', 'Firefox', '日本', 6), +(1002, '/products', '2024-01-17', '13:20:00', 28, 'Desktop', 'Edge', '美国', 7), +(1005, '/contact', '2024-01-18', '10:40:00', 12, 'Mobile', 'Safari', '加拿大', 2), +(1003, '/home', '2024-01-18', '15:25:00', 22, 'Desktop', 'Chrome', '英国', 4), +(1006, '/products', '2024-01-19', '09:10:00', 40, 'Tablet', 'Safari', '澳大利亚', 9), +(1001, '/home', '2024-01-19', '14:35:00', 18, 'Mobile', 'Chrome', '中国', 3), +(1007, '/login', '2024-01-20', '11:20:00', 8, 'Desktop', 'Firefox', '德国', 1), +(1002, '/dashboard', '2024-01-20', '16:45:00', 55, 'Desktop', 'Chrome', '美国', 12); + +-- 插入库存数据 +INSERT INTO inventory (product_id, product_name, category, supplier, stock_quantity, reorder_level, unit_cost, last_restock_date, warehouse_location) VALUES +(101, 'MacBook Pro', '笔记本', 'Apple', 45, 20, 10000.00, '2024-01-10', '北京仓库'), +(102, 'iPhone 15', '手机', 'Apple', 120, 50, 4500.00, '2024-01-12', '上海仓库'), +(103, 'iPad Air', '平板', 'Apple', 80, 30, 3200.00, '2024-01-08', '深圳仓库'), +(104, 'AirPods Pro', '耳机', 'Apple', 200, 80, 1500.00, '2024-01-15', '北京仓库'), +(105, 'Apple Watch', '智能手表', 'Apple', 60, 25, 2400.00, '2024-01-05', '广州仓库'), +(106, 'MacBook Air', '笔记本', 'Apple', 35, 15, 7200.00, '2024-01-20', '上海仓库'), +(107, 'iMac', '台式机', 'Apple', 15, 10, 12800.00, '2024-01-18', '深圳仓库'), +(108, 'iPad Pro', '平板', 'Apple', 25, 15, 6400.00, '2024-01-22', '北京仓库'), +(109, 'Studio Display', '显示器', 'Apple', 12, 8, 9600.00, '2024-01-25', '广州仓库'), +(110, 'Mac Mini', '台式机', 'Apple', 30, 12, 4800.00, '2024-01-28', '杭州仓库'); +``` + +## 基础分组查询 + +### 1. 简单分组统计 + +```sql +-- 按产品类别统计销售数量 +SELECT category, SUM(quantity) AS total_quantity +FROM sales_orders +GROUP BY category; +``` + +**结果:** +``` ++----------+----------------+ +| category | total_quantity | ++----------+----------------+ +| 笔记本 | 37 | +| 手机 | 120 | +| 平板 | 73 | +| 耳机 | 180 | +| 智能手表 | 55 | +| 台式机 | 8 | +| 显示器 | 6 | ++----------+----------------+ +``` + +### 2. 按部门统计员工信息 + +```sql +-- 按部门统计员工数量和平均薪资 +SELECT department, + COUNT(*) AS emp_count, + ROUND(AVG(salary), 2) AS avg_salary, + MIN(salary) AS min_salary, + MAX(salary) AS max_salary +FROM employees +GROUP BY department; +``` + +**结果:** +``` ++----------+-----------+------------+------------+------------+ +| department | emp_count | avg_salary | min_salary | max_salary | ++----------+-----------+------------+------------+------------+ +| 技术部 | 5 | 17600.00 | 8000.00 | 25000.00 | +| 产品部 | 3 | 16000.00 | 12000.00 | 20000.00 | +| 销售部 | 3 | 14000.00 | 10000.00 | 18000.00 | +| 人事部 | 2 | 13500.00 | 11000.00 | 16000.00 | +| 财务部 | 2 | 16000.00 | 13000.00 | 19000.00 | ++----------+-----------+------------+------------+------------+ +``` + +### 3. 按日期分组统计 + +```sql +-- 按月份统计订单数量和销售额 +SELECT DATE_FORMAT(order_date, '%Y-%m') AS order_month, + COUNT(*) AS order_count, + SUM(quantity * unit_price * (1 - discount_rate)) AS total_sales +FROM sales_orders +GROUP BY DATE_FORMAT(order_date, '%Y-%m') +ORDER BY order_month; +``` + +**结果:** +``` ++-------------+-------------+-------------+ +| order_month | order_count | total_sales | ++-------------+-------------+-------------+ +| 2024-01 | 6 | 912393.00 | +| 2024-02 | 9 | 809328.40 | ++-------------+-------------+-------------+ +``` + +## 聚合函数详解 + +### 1. COUNT() 函数详解 + +```sql +-- COUNT 的不同用法 +SELECT + COUNT(*) AS total_rows, -- 总行数 + COUNT(manager_id) AS has_manager, -- 非NULL值数量 + COUNT(DISTINCT department) AS dept_count, -- 去重计数 + COUNT(CASE WHEN salary > 15000 THEN 1 END) AS high_salary_count -- 条件计数 +FROM employees; +``` + +**结果:** +``` ++------------+-------------+------------+-------------------+ +| total_rows | has_manager | dept_count | high_salary_count | ++------------+-------------+------------+-------------------+ +| 15 | 10 | 5 | 8 | ++------------+-------------+------------+-------------------+ +``` + +### 2. SUM() 和 AVG() 函数 + +```sql +-- 按销售代表统计业绩 +SELECT sales_rep, + COUNT(*) AS order_count, + SUM(quantity) AS total_quantity, + SUM(quantity * unit_price * (1 - discount_rate)) AS total_sales, + ROUND(AVG(quantity * unit_price * (1 - discount_rate)), 2) AS avg_order_value +FROM sales_orders +GROUP BY sales_rep +ORDER BY total_sales DESC; +``` + +**结果:** +``` ++----------+-------------+----------------+-------------+-----------------+ +| sales_rep | order_count | total_quantity | total_sales | avg_order_value | ++----------+-------------+----------------+-------------+-----------------+ +| 张三 | 2 | 60 | 423447.00 | 211723.50 | +| 李四 | 2 | 35 | 318969.00 | 159484.50 | +| 王五 | 2 | 130 | 344562.00 | 172281.00 | +| 赵六 | 2 | 60 | 206187.00 | 103093.50 | +| 钱七 | 2 | 92 | 258291.60 | 129145.80 | +| 孙八 | 2 | 48 | 355464.00 | 177732.00 | +| 周九 | 2 | 48 | 222777.60 | 111388.80 | +| 吴十 | 1 | 6 | 70622.80 | 70622.80 | ++----------+-------------+----------------+-------------+-----------------+ +``` + +### 3. MAX() 和 MIN() 函数 + +```sql +-- 按班级统计成绩分布 +SELECT class_id, + COUNT(*) AS student_count, + MAX(score) AS highest_score, + MIN(score) AS lowest_score, + ROUND(AVG(score), 2) AS avg_score, + MAX(score) - MIN(score) AS score_range +FROM student_scores +GROUP BY class_id +ORDER BY class_id; +``` + +**结果:** +``` ++----------+---------------+---------------+--------------+-----------+-------------+ +| class_id | student_count | highest_score | lowest_score | avg_score | score_range | ++----------+---------------+---------------+--------------+-----------+-------------+ +| 1 | 8 | 95 | 78 | 86.75 | 17 | +| 2 | 7 | 90 | 76 | 82.71 | 14 | +| 3 | 3 | 93 | 87 | 90.00 | 6 | ++----------+---------------+---------------+--------------+-----------+-------------+ +``` + +### 4. GROUP_CONCAT() 函数 + +```sql +-- 按部门列出所有员工姓名 +SELECT department, + COUNT(*) AS emp_count, + GROUP_CONCAT(name ORDER BY salary DESC) AS employees, + GROUP_CONCAT(DISTINCT city) AS cities +FROM employees +GROUP BY department; +``` + +**结果:** +``` ++----------+-----------+------------------------------------------+----------+ +| department | emp_count | employees | cities | ++----------+-----------+------------------------------------------+----------+ +| 技术部 | 5 | 张经理,李架构师,王工程师,赵工程师,钱实习生 | 北京 | +| 产品部 | 3 | 孙经理,周产品,吴助理 | 上海 | +| 销售部 | 3 | 郑经理,刘销售,陈销售 | 深圳 | +| 人事部 | 2 | 林经理,黄专员 | 广州 | +| 财务部 | 2 | 何经理,魏会计 | 杭州 | ++----------+-----------+------------------------------------------+----------+ +``` + +## HAVING 子句使用 + +### 1. HAVING 基础用法 + +```sql +-- 查找员工数量大于2的部门 +SELECT department, + COUNT(*) AS emp_count, + ROUND(AVG(salary), 2) AS avg_salary +FROM employees +GROUP BY department +HAVING COUNT(*) > 2 +ORDER BY emp_count DESC; +``` + +**结果:** +``` ++----------+-----------+------------+ +| department | emp_count | avg_salary | ++----------+-----------+------------+ +| 技术部 | 5 | 17600.00 | +| 产品部 | 3 | 16000.00 | +| 销售部 | 3 | 14000.00 | ++----------+-----------+------------+ +``` + +### 2. HAVING 与 WHERE 的区别 + +```sql +-- 错误用法:WHERE 不能使用聚合函数 +-- SELECT department, COUNT(*) FROM employees WHERE COUNT(*) > 2 GROUP BY department; + +-- 正确用法:先过滤再分组 +SELECT department, + COUNT(*) AS emp_count, + ROUND(AVG(salary), 2) AS avg_salary +FROM employees +WHERE salary > 12000 -- 先过滤薪资大于12000的员工 +GROUP BY department +HAVING COUNT(*) >= 2 -- 再过滤员工数量>=2的部门 +ORDER BY avg_salary DESC; +``` + +**结果:** +``` ++----------+-----------+------------+ +| department | emp_count | avg_salary | ++----------+-----------+------------+ +| 技术部 | 3 | 21666.67 | +| 产品部 | 2 | 18000.00 | +| 财务部 | 2 | 16000.00 | ++----------+-----------+------------+ +``` + +### 3. 复杂 HAVING 条件 + +```sql +-- 查找高价值客户(订单总额>30万且订单数量>1) +SELECT customer_name, + COUNT(*) AS order_count, + SUM(quantity * unit_price * (1 - discount_rate)) AS total_sales, + ROUND(AVG(quantity * unit_price * (1 - discount_rate)), 2) AS avg_order_value +FROM sales_orders +GROUP BY customer_name +HAVING COUNT(*) > 1 AND SUM(quantity * unit_price * (1 - discount_rate)) > 300000 +ORDER BY total_sales DESC; +``` + +**结果:** +``` ++--------------+-------------+-------------+-----------------+ +| customer_name | order_count | total_sales | avg_order_value | ++--------------+-------------+-------------+-----------------+ +| 阿里巴巴 | 2 | 423447.00 | 211723.50 | +| 腾讯 | 2 | 318969.00 | 159484.50 | +| 百度 | 2 | 344562.00 | 172281.00 | +| 滴滴 | 2 | 355464.00 | 177732.00 | ++-------------+-------------+-------------+-----------------+ +``` + +## 多列分组查询 + +### 1. 双列分组 + +```sql +-- 按地区和销售代表分组统计 +SELECT region, sales_rep, + COUNT(*) AS order_count, + SUM(quantity) AS total_quantity, + SUM(quantity * unit_price * (1 - discount_rate)) AS total_sales +FROM sales_orders +GROUP BY region, sales_rep +ORDER BY region, total_sales DESC; +``` + +**结果:** +``` ++--------+-----------+-------------+----------------+-------------+ +| region | sales_rep | order_count | total_quantity | total_sales | ++--------+-----------+-------------+----------------+-------------+ +| 华北 | 王五 | 2 | 130 | 344562.00 | +| 华北 | 赵六 | 2 | 60 | 206187.00 | +| 华北 | 周九 | 2 | 48 | 222777.60 | +| 华东 | 张三 | 2 | 60 | 423447.00 | +| 华东 | 钱七 | 2 | 92 | 258291.60 | +| 华南 | 李四 | 2 | 35 | 318969.00 | +| 华南 | 孙八 | 2 | 48 | 355464.00 | +| 华南 | 吴十 | 1 | 6 | 70622.80 | ++--------+-----------+-------------+----------------+-------------+ +``` + +### 2. 三列分组 + +```sql +-- 按年龄段、性别、部门分组统计员工 +SELECT + CASE + WHEN age < 25 THEN '24岁以下' + WHEN age <= 30 THEN '25-30岁' + WHEN age <= 35 THEN '31-35岁' + ELSE '35岁以上' + END AS age_group, + gender, + department, + COUNT(*) AS emp_count, + ROUND(AVG(salary), 2) AS avg_salary +FROM employees +GROUP BY + CASE + WHEN age < 25 THEN '24岁以下' + WHEN age <= 30 THEN '25-30岁' + WHEN age <= 35 THEN '31-35岁' + ELSE '35岁以上' + END, + gender, + department +HAVING COUNT(*) >= 1 +ORDER BY age_group, gender, department; +``` + +### 3. 时间维度分组 + +```sql +-- 按年、月、产品类别分组统计 +SELECT + YEAR(order_date) AS order_year, + MONTH(order_date) AS order_month, + category, + COUNT(*) AS order_count, + SUM(quantity) AS total_quantity, + ROUND(SUM(quantity * unit_price * (1 - discount_rate)), 2) AS total_sales +FROM sales_orders +GROUP BY YEAR(order_date), MONTH(order_date), category +ORDER BY order_year, order_month, total_sales DESC; +``` + +## 复杂分组统计 + +### 1. 嵌套分组查询 + +```sql +-- 查找每个部门薪资最高的员工信息 +SELECT e1.* +FROM employees e1 +WHERE e1.salary = ( + SELECT MAX(e2.salary) + FROM employees e2 + WHERE e2.department = e1.department +) +ORDER BY e1.department, e1.salary DESC; +``` + +### 2. 窗口函数与分组 + +```sql +-- 计算每个员工在部门内的薪资排名 +SELECT name, department, salary, + ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank, + ROUND(salary / AVG(salary) OVER (PARTITION BY department) * 100, 2) AS salary_ratio +FROM employees +ORDER BY department, dept_rank; +``` + +### 3. 数据透视表效果 + +```sql +-- 按设备类型和浏览器统计访问情况 +SELECT device_type, + SUM(CASE WHEN browser = 'Chrome' THEN page_views ELSE 0 END) AS Chrome, + SUM(CASE WHEN browser = 'Safari' THEN page_views ELSE 0 END) AS Safari, + SUM(CASE WHEN browser = 'Firefox' THEN page_views ELSE 0 END) AS Firefox, + SUM(CASE WHEN browser = 'Edge' THEN page_views ELSE 0 END) AS Edge, + SUM(page_views) AS Total +FROM web_logs +GROUP BY device_type +ORDER BY Total DESC; +``` + +**结果:** +``` ++-------------+--------+--------+---------+------+-------+ +| device_type | Chrome | Safari | Firefox | Edge | Total | ++-------------+--------+--------+---------+------+-------+ +| Desktop | 24 | 0 | 1 | 7 | 32 | +| Mobile | 6 | 5 | 6 | 0 | 17 | +| Tablet | 4 | 9 | 0 | 0 | 13 | ++-------------+--------+--------+---------+------+-------+ +``` + +### 4. 动态分组条件 + +```sql +-- 按库存状态分组统计产品 +SELECT + CASE + WHEN stock_quantity <= reorder_level THEN '需要补货' + WHEN stock_quantity <= reorder_level * 2 THEN '库存偏低' + ELSE '库存充足' + END AS stock_status, + COUNT(*) AS product_count, + SUM(stock_quantity) AS total_stock, + ROUND(AVG(unit_cost), 2) AS avg_cost +FROM inventory +GROUP BY + CASE + WHEN stock_quantity <= reorder_level THEN '需要补货' + WHEN stock_quantity <= reorder_level * 2 THEN '库存偏低' + ELSE '库存充足' + END +ORDER BY + CASE + WHEN stock_status = '需要补货' THEN 1 + WHEN stock_status = '库存偏低' THEN 2 + ELSE 3 + END; +``` + +## 面试题和实际案例 + +### 面试题1:连续登录用户分析 + +**题目**:找出连续3天都有访问记录的用户。 + +```sql +-- 解答:使用窗口函数和分组 +WITH daily_users AS ( + SELECT DISTINCT user_id, visit_date + FROM web_logs +), +user_sequences AS ( + SELECT user_id, visit_date, + ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY visit_date) as rn, + DATE_SUB(visit_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY visit_date) DAY) as group_date + FROM daily_users +), +consecutive_groups AS ( + SELECT user_id, group_date, COUNT(*) as consecutive_days + FROM user_sequences + GROUP BY user_id, group_date + HAVING COUNT(*) >= 3 +) +SELECT DISTINCT cg.user_id +FROM consecutive_groups cg; +``` + +### 面试题2:同比增长率计算 + +**题目**:计算每个月的销售额同比增长率。 + +```sql +-- 解答:使用自连接和分组 +WITH monthly_sales AS ( + SELECT + DATE_FORMAT(order_date, '%Y-%m') as month, + SUM(quantity * unit_price * (1 - discount_rate)) as total_sales + FROM sales_orders + GROUP BY DATE_FORMAT(order_date, '%Y-%m') +) +SELECT + current_month.month, + current_month.total_sales as current_sales, + last_year.total_sales as last_year_sales, + ROUND( + (current_month.total_sales - IFNULL(last_year.total_sales, 0)) / + IFNULL(last_year.total_sales, current_month.total_sales) * 100, 2 + ) as growth_rate +FROM monthly_sales current_month +LEFT JOIN monthly_sales last_year + ON DATE_FORMAT(DATE_SUB(STR_TO_DATE(CONCAT(current_month.month, '-01'), '%Y-%m-%d'), INTERVAL 1 YEAR), '%Y-%m') = last_year.month +ORDER BY current_month.month; +``` + +### 面试题3:帕累托分析(80/20法则) + +**题目**:找出贡献80%销售额的前20%客户。 + +```sql +-- 解答:使用窗口函数计算累计占比 +WITH customer_sales AS ( + SELECT customer_name, + SUM(quantity * unit_price * (1 - discount_rate)) as total_sales + FROM sales_orders + GROUP BY customer_name +), +customer_ranking AS ( + SELECT customer_name, total_sales, + ROW_NUMBER() OVER (ORDER BY total_sales DESC) as rank_num, + COUNT(*) OVER () as total_customers, + SUM(total_sales) OVER () as grand_total, + SUM(total_sales) OVER (ORDER BY total_sales DESC) as cumulative_sales + FROM customer_sales +), +customer_contribution AS ( + SELECT *, + ROUND(rank_num / total_customers * 100, 2) as customer_percentile, + ROUND(cumulative_sales / grand_total * 100, 2) as sales_percentile + FROM customer_ranking +) +SELECT customer_name, total_sales, customer_percentile, sales_percentile +FROM customer_contribution +WHERE sales_percentile <= 80 +ORDER BY total_sales DESC; +``` + +### 实际案例1:用户行为分析 + +**场景**:电商网站需要分析用户访问行为,优化用户体验。 + +```sql +-- 综合用户行为分析 +SELECT + country, + device_type, + COUNT(DISTINCT user_id) as unique_users, + COUNT(*) as total_sessions, + ROUND(COUNT(*) / COUNT(DISTINCT user_id), 2) as sessions_per_user, + ROUND(AVG(session_duration), 2) as avg_session_duration, + SUM(page_views) as total_page_views, + ROUND(SUM(page_views) / COUNT(*), 2) as pages_per_session, + ROUND(SUM(session_duration) / 60, 2) as total_hours +FROM web_logs +GROUP BY country, device_type +HAVING COUNT(DISTINCT user_id) >= 1 +ORDER BY unique_users DESC, avg_session_duration DESC; +``` + +### 实际案例2:销售业绩分析 + +**场景**:销售部门需要全面分析销售业绩,制定奖励策略。 + +```sql +-- 销售代表综合业绩分析 +WITH sales_performance AS ( + SELECT sales_rep, + COUNT(*) as order_count, + COUNT(DISTINCT customer_name) as customer_count, + SUM(quantity) as total_quantity, + SUM(quantity * unit_price * (1 - discount_rate)) as total_sales, + ROUND(AVG(quantity * unit_price * (1 - discount_rate)), 2) as avg_order_value, + MAX(quantity * unit_price * (1 - discount_rate)) as max_order_value, + COUNT(DISTINCT category) as category_diversity + FROM sales_orders + GROUP BY sales_rep +), +performance_ranking AS ( + SELECT *, + RANK() OVER (ORDER BY total_sales DESC) as sales_rank, + RANK() OVER (ORDER BY customer_count DESC) as customer_rank, + RANK() OVER (ORDER BY avg_order_value DESC) as avg_value_rank, + ROUND(total_sales / SUM(total_sales) OVER () * 100, 2) as sales_share + FROM sales_performance +) +SELECT sales_rep, total_sales, customer_count, avg_order_value, + sales_rank, customer_rank, sales_share, + CASE + WHEN sales_rank <= 2 THEN '金牌销售' + WHEN sales_rank <= 4 THEN '银牌销售' + ELSE '铜牌销售' + END as performance_level +FROM performance_ranking +ORDER BY sales_rank; +``` + +### 实际案例3:库存管理优化 + +**场景**:仓库管理需要优化库存配置,减少缺货和积压。 + +```sql +-- 库存分析和补货建议 +SELECT + category, + warehouse_location, + COUNT(*) as product_count, + SUM(stock_quantity) as total_stock, + SUM(CASE WHEN stock_quantity <= reorder_level THEN 1 ELSE 0 END) as need_reorder, + ROUND(AVG(stock_quantity), 2) as avg_stock, + ROUND(AVG(unit_cost), 2) as avg_cost, + SUM(stock_quantity * unit_cost) as inventory_value, + ROUND(SUM(CASE WHEN stock_quantity <= reorder_level THEN unit_cost * reorder_level ELSE 0 END), 2) as reorder_investment +FROM inventory +GROUP BY category, warehouse_location +HAVING COUNT(*) >= 1 +ORDER BY inventory_value DESC, need_reorder DESC; +``` + +### 面试题4:数据质量检查 + +**题目**:检查数据中的异常情况。 + +```sql +-- 多维度数据质量检查 +SELECT + 'sales_orders' as table_name, + 'order_date' as check_field, + COUNT(*) as total_records, + SUM(CASE WHEN order_date IS NULL THEN 1 ELSE 0 END) as null_count, + SUM(CASE WHEN order_date > CURDATE() THEN 1 ELSE 0 END) as future_date_count, + MIN(order_date) as min_date, + MAX(order_date) as max_date +FROM sales_orders + +UNION ALL + +SELECT + 'employees' as table_name, + 'salary' as check_field, + COUNT(*) as total_records, + SUM(CASE WHEN salary IS NULL THEN 1 ELSE 0 END) as null_count, + SUM(CASE WHEN salary <= 0 THEN 1 ELSE 0 END) as invalid_salary_count, + MIN(salary) as min_salary, + MAX(salary) as max_salary +FROM employees; +``` + +## 性能优化和最佳实践 + +### 1. 索引优化 + +```sql +-- 为分组字段创建索引 +CREATE INDEX idx_sales_orders_category ON sales_orders(category); +CREATE INDEX idx_sales_orders_sales_rep ON sales_orders(sales_rep); +CREATE INDEX idx_employees_department ON employees(department); + +-- 复合索引优化分组查询 +CREATE INDEX idx_sales_orders_date_region ON sales_orders(order_date, region); +CREATE INDEX idx_web_logs_user_date ON web_logs(user_id, visit_date); +``` + +### 2. 查询优化技巧 + +```sql +-- ✅ 推荐:使用WHERE过滤再分组 +SELECT department, COUNT(*) +FROM employees +WHERE salary > 10000 -- 先过滤 +GROUP BY department; + +-- ❌ 避免:分组后再过滤 +SELECT department, COUNT(*) +FROM employees +GROUP BY department +HAVING AVG(salary) > 10000; -- 这会计算所有组的平均值 +``` + +### 3. 避免不必要的分组 + +```sql +-- ❌ 不必要的分组 +SELECT customer_name, SUM(quantity) +FROM sales_orders +WHERE customer_name = '阿里巴巴' +GROUP BY customer_name; + +-- ✅ 优化:直接使用WHERE +SELECT '阿里巴巴' as customer_name, SUM(quantity) +FROM sales_orders +WHERE customer_name = '阿里巴巴'; +``` + +### 4. 大数据量分组优化 + +```sql +-- 使用分区表优化大数据量分组 +-- CREATE TABLE sales_orders_partitioned ( +-- ... +-- ) PARTITION BY RANGE (YEAR(order_date)) ( +-- PARTITION p2023 VALUES LESS THAN (2024), +-- PARTITION p2024 VALUES LESS THAN (2025) +-- ); + +-- 优化GROUP BY的内存使用 +SET SESSION group_concat_max_len = 1000000; +SET SESSION tmp_table_size = 256000000; +SET SESSION max_heap_table_size = 256000000; +``` + +### 5. 监控和调优 + +```sql +-- 查看分组查询的执行计划 +EXPLAIN SELECT department, COUNT(*), AVG(salary) +FROM employees +GROUP BY department; + +-- 监控分组查询性能 +SELECT + sql_text, + exec_count, + avg_timer_wait/1000000000000 as avg_time_sec, + sum_rows_examined/exec_count as avg_rows_examined +FROM performance_schema.events_statements_summary_by_digest +WHERE sql_text LIKE '%GROUP BY%' +ORDER BY avg_timer_wait DESC +LIMIT 10; +``` + +### 6. 最佳实践总结 + +```sql +-- ✅ 好的GROUP BY实践 +SELECT + category, -- 分组字段 + COUNT(*) as order_count, -- 明确的聚合函数 + SUM(quantity) as total_qty -- 有意义的别名 +FROM sales_orders +WHERE order_date >= '2024-01-01' -- 先过滤数据 +GROUP BY category -- 简洁的分组 +HAVING COUNT(*) > 1 -- 分组后过滤 +ORDER BY total_qty DESC -- 有序输出 +LIMIT 10; -- 限制结果数量 + +-- ❌ 避免的问题 +-- 1. 在SELECT中使用非分组字段(MySQL 5.7+会报错) +-- 2. GROUP BY使用复杂表达式而不创建索引 +-- 3. 不必要的HAVING条件 +-- 4. 大量数据不加WHERE条件直接分组 +``` + +--- + +**总结:** +- GROUP BY 是数据分析的核心工具,配合聚合函数使用 +- 理解执行顺序:WHERE → GROUP BY → HAVING → SELECT → ORDER BY +- 合理使用索引可以显著提升分组查询性能 +- HAVING 用于过滤分组结果,WHERE 用于过滤原始数据 +- 复杂分析可以结合窗口函数和子查询实现 +- 注意数据类型和NULL值的处理 \ No newline at end of file diff --git a/MySQL_HAVING语法使用文档.md b/MySQL_HAVING语法使用文档.md new file mode 100644 index 0000000..c2c1657 --- /dev/null +++ b/MySQL_HAVING语法使用文档.md @@ -0,0 +1,951 @@ +# MySQL HAVING 语法使用文档 + +## 目录 +1. [HAVING 基础语法](#having-基础语法) +2. [HAVING 规则和特性](#having-规则和特性) +3. [HAVING vs WHERE 详细对比](#having-vs-where-详细对比) +4. [示例数据准备](#示例数据准备) +5. [基础 HAVING 查询](#基础-having-查询) +6. [复杂 HAVING 条件](#复杂-having-条件) +7. [HAVING 与聚合函数组合](#having-与聚合函数组合) +8. [多层嵌套和子查询](#多层嵌套和子查询) +9. [面试题和实际案例](#面试题和实际案例) +10. [性能优化和最佳实践](#性能优化和最佳实践) + +## HAVING 基础语法 + +HAVING 子句用于过滤 GROUP BY 分组后的结果,类似于 WHERE 子句,但作用于分组之后。 + +```sql +SELECT column1, aggregate_function(column2) +FROM table_name +[WHERE condition] +GROUP BY column1, column2, ... +HAVING group_condition +[ORDER BY column] +[LIMIT number]; +``` + +**语法要点:** +- HAVING 必须在 GROUP BY 之后 +- HAVING 条件可以使用聚合函数 +- HAVING 可以引用 SELECT 列表中的别名 +- HAVING 可以与 WHERE 同时使用 + +## HAVING 规则和特性 + +### 1. **执行顺序** +``` +FROM → WHERE → GROUP BY → 聚合计算 → HAVING → SELECT → ORDER BY → LIMIT +``` + +### 2. **可用条件类型** +- 聚合函数条件:`COUNT(*) > 5` +- 分组字段条件:`department = '技术部'` +- 聚合函数比较:`AVG(salary) > MAX(bonus)` +- 复合条件:`COUNT(*) > 2 AND AVG(score) >= 80` + +### 3. **与 WHERE 的区别** +| 特性 | WHERE | HAVING | +|------|-------|--------| +| 作用时机 | 分组前过滤行 | 分组后过滤组 | +| 可用函数 | 不能使用聚合函数 | 可以使用聚合函数 | +| 性能 | 更高效(减少分组数据) | 相对较低(先分组再过滤) | +| 使用场景 | 过滤原始数据 | 过滤聚合结果 | + +## HAVING vs WHERE 详细对比 + +### 1. 基础区别演示 + +```sql +-- 示例数据:员工表 +CREATE TABLE demo_employees ( + id INT PRIMARY KEY, + name VARCHAR(50), + department VARCHAR(30), + salary DECIMAL(10,2), + bonus DECIMAL(8,2) +); + +INSERT INTO demo_employees VALUES +(1, '张三', '技术部', 15000, 3000), +(2, '李四', '技术部', 18000, 4000), +(3, '王五', '销售部', 12000, 8000), +(4, '赵六', '销售部', 14000, 6000), +(5, '钱七', '技术部', 16000, 3500), +(6, '孙八', '人事部', 13000, 2000); +``` + +```sql +-- WHERE:先过滤薪资 > 14000 的员工,再分组统计 +SELECT department, COUNT(*) as emp_count, AVG(salary) as avg_salary +FROM demo_employees +WHERE salary > 14000 +GROUP BY department; +``` + +**结果:** +``` ++----------+-----------+------------+ +| department | emp_count | avg_salary | ++----------+-----------+------------+ +| 技术部 | 3 | 16333.33 | +| 销售部 | 1 | 14000.00 | ++----------+-----------+------------+ +``` + +```sql +-- HAVING:先分组统计,再过滤平均薪资 > 14000 的部门 +SELECT department, COUNT(*) as emp_count, AVG(salary) as avg_salary +FROM demo_employees +GROUP BY department +HAVING AVG(salary) > 14000; +``` + +**结果:** +``` ++----------+-----------+------------+ +| department | emp_count | avg_salary | ++----------+-----------+------------+ +| 技术部 | 3 | 16333.33 | ++----------+-----------+------------+ +``` + +### 2. 同时使用 WHERE 和 HAVING + +```sql +-- 组合使用:先过滤 bonus > 2500 的员工,分组后再过滤员工数量 > 1 的部门 +SELECT department, + COUNT(*) as emp_count, + ROUND(AVG(salary), 2) as avg_salary, + ROUND(AVG(bonus), 2) as avg_bonus +FROM demo_employees +WHERE bonus > 2500 -- 先过滤原始数据 +GROUP BY department +HAVING COUNT(*) > 1; -- 再过滤分组结果 +``` + +**结果:** +``` ++----------+-----------+------------+-----------+ +| department | emp_count | avg_salary | avg_bonus | ++----------+-----------+------------+-----------+ +| 技术部 | 3 | 16333.33 | 3500.00 | +| 销售部 | 2 | 13000.00 | 7000.00 | ++----------+-----------+------------+-----------+ +``` + +## 示例数据准备 + +### 创建业务场景表 + +```sql +-- 销售业绩表 +CREATE TABLE sales_performance ( + id INT PRIMARY KEY AUTO_INCREMENT, + sales_rep VARCHAR(50), + region VARCHAR(30), + product_category VARCHAR(50), + sale_date DATE, + amount DECIMAL(10,2), + quantity INT, + customer_type ENUM('新客户', '老客户', 'VIP客户'), + commission_rate DECIMAL(3,2) +); + +-- 课程选课表 +CREATE TABLE course_enrollments ( + student_id INT, + student_name VARCHAR(50), + course_id VARCHAR(20), + course_name VARCHAR(100), + credits INT, + score DECIMAL(4,1), + semester VARCHAR(20), + teacher VARCHAR(30), + department VARCHAR(30), + PRIMARY KEY (student_id, course_id, semester) +); + +-- 电商订单表 +CREATE TABLE ecommerce_orders ( + order_id INT PRIMARY KEY AUTO_INCREMENT, + customer_id INT, + customer_segment VARCHAR(20), + product_id INT, + category VARCHAR(50), + subcategory VARCHAR(50), + order_date DATE, + ship_date DATE, + quantity INT, + unit_price DECIMAL(8,2), + discount DECIMAL(3,2), + profit DECIMAL(8,2), + region VARCHAR(30), + state VARCHAR(30) +); + +-- 网站流量分析表 +CREATE TABLE website_analytics ( + session_id VARCHAR(50) PRIMARY KEY, + user_id INT, + visit_date DATE, + page_views INT, + session_duration INT, -- 秒 + bounce_rate DECIMAL(3,2), + conversion_flag BOOLEAN, + traffic_source VARCHAR(30), + device_category VARCHAR(20), + country VARCHAR(30), + revenue DECIMAL(8,2) +); + +-- 员工绩效表 +CREATE TABLE employee_performance ( + emp_id INT, + name VARCHAR(50), + department VARCHAR(30), + position VARCHAR(50), + quarter VARCHAR(10), + kpi_score DECIMAL(3,1), + project_count INT, + overtime_hours INT, + team_rating DECIMAL(2,1), + bonus DECIMAL(8,2), + PRIMARY KEY (emp_id, quarter) +); +``` + +### 插入示例数据 + +```sql +-- 插入销售业绩数据 +INSERT INTO sales_performance (sales_rep, region, product_category, sale_date, amount, quantity, customer_type, commission_rate) VALUES +('张明', '华北', '电子产品', '2024-01-15', 15000.00, 5, '新客户', 0.08), +('张明', '华北', '家电', '2024-01-20', 8000.00, 2, '老客户', 0.06), +('张明', '华北', '电子产品', '2024-01-25', 25000.00, 8, 'VIP客户', 0.10), +('李华', '华东', '服装', '2024-01-18', 12000.00, 20, '新客户', 0.07), +('李华', '华东', '电子产品', '2024-01-22', 18000.00, 6, 'VIP客户', 0.09), +('李华', '华东', '服装', '2024-01-28', 9000.00, 15, '老客户', 0.05), +('王强', '华南', '家电', '2024-01-16', 22000.00, 4, 'VIP客户', 0.08), +('王强', '华南', '电子产品', '2024-01-24', 16000.00, 7, '新客户', 0.07), +('王强', '华南', '家电', '2024-01-30', 11000.00, 3, '老客户', 0.06), +('赵丽', '华西', '服装', '2024-01-19', 7000.00, 12, '新客户', 0.06), +('赵丽', '华西', '服装', '2024-01-26', 13000.00, 18, 'VIP客户', 0.08), +('孙涛', '华北', '电子产品', '2024-01-21', 20000.00, 6, 'VIP客户', 0.09), +('孙涛', '华北', '家电', '2024-01-27', 14000.00, 5, '老客户', 0.07); + +-- 插入课程选课数据 +INSERT INTO course_enrollments VALUES +(1001, '张小明', 'CS101', '计算机科学导论', 3, 85.5, '2024春', '王教授', '计算机系'), +(1001, '张小明', 'MATH201', '高等数学', 4, 78.0, '2024春', '李教授', '数学系'), +(1001, '张小明', 'ENG101', '大学英语', 2, 88.5, '2024春', '陈教授', '外语系'), +(1002, '李小红', 'CS101', '计算机科学导论', 3, 92.0, '2024春', '王教授', '计算机系'), +(1002, '李小红', 'MATH201', '高等数学', 4, 86.5, '2024春', '李教授', '数学系'), +(1002, '李小红', 'PHYS101', '大学物理', 3, 79.0, '2024春', '张教授', '物理系'), +(1003, '王小刚', 'CS102', '程序设计', 3, 76.5, '2024春', '赵教授', '计算机系'), +(1003, '王小刚', 'MATH201', '高等数学', 4, 82.0, '2024春', '李教授', '数学系'), +(1003, '王小刚', 'ENG101', '大学英语', 2, 74.5, '2024春', '陈教授', '外语系'), +(1004, '赵小丽', 'CS101', '计算机科学导论', 3, 89.0, '2024春', '王教授', '计算机系'), +(1004, '赵小丽', 'MATH202', '线性代数', 3, 91.5, '2024春', '孙教授', '数学系'), +(1004, '赵小丽', 'PHYS101', '大学物理', 3, 84.5, '2024春', '张教授', '物理系'), +(1005, '钱小伟', 'CS102', '程序设计', 3, 95.0, '2024春', '赵教授', '计算机系'), +(1005, '钱小伟', 'MATH202', '线性代数', 3, 88.0, '2024春', '孙教授', '数学系'), +(1005, '钱小伟', 'ENG102', '英语写作', 2, 85.5, '2024春', '周教授', '外语系'); + +-- 插入电商订单数据 +INSERT INTO ecommerce_orders (customer_id, customer_segment, product_id, category, subcategory, order_date, ship_date, quantity, unit_price, discount, profit, region, state) VALUES +(1001, 'Consumer', 2001, 'Technology', 'Phones', '2024-01-15', '2024-01-17', 2, 999.99, 0.10, 400.00, 'East', 'New York'), +(1001, 'Consumer', 2002, 'Technology', 'Accessories', '2024-01-16', '2024-01-18', 5, 29.99, 0.05, 50.00, 'East', 'New York'), +(1002, 'Corporate', 2003, 'Office Supplies', 'Storage', '2024-01-18', '2024-01-20', 10, 15.99, 0.15, 80.00, 'West', 'California'), +(1002, 'Corporate', 2004, 'Furniture', 'Chairs', '2024-01-19', '2024-01-22', 3, 299.99, 0.20, 300.00, 'West', 'California'), +(1003, 'Home Office', 2005, 'Technology', 'Computers', '2024-01-20', '2024-01-23', 1, 1299.99, 0.08, 200.00, 'Central', 'Texas'), +(1003, 'Home Office', 2006, 'Office Supplies', 'Paper', '2024-01-21', '2024-01-24', 20, 12.99, 0.00, 100.00, 'Central', 'Texas'), +(1004, 'Consumer', 2007, 'Technology', 'Phones', '2024-01-22', '2024-01-25', 1, 1199.99, 0.05, 300.00, 'East', 'Florida'), +(1004, 'Consumer', 2008, 'Furniture', 'Tables', '2024-01-23', '2024-01-26', 2, 199.99, 0.10, 150.00, 'East', 'Florida'), +(1005, 'Corporate', 2009, 'Office Supplies', 'Binders', '2024-01-24', '2024-01-27', 50, 8.99, 0.25, 200.00, 'West', 'Oregon'), +(1005, 'Corporate', 2010, 'Technology', 'Accessories', '2024-01-25', '2024-01-28', 15, 49.99, 0.12, 180.00, 'West', 'Oregon'); + +-- 插入网站流量数据 +INSERT INTO website_analytics VALUES +('sess_001', 1001, '2024-01-15', 8, 450, 0.00, TRUE, 'Google', 'Desktop', 'USA', 299.99), +('sess_002', 1002, '2024-01-15', 3, 120, 1.00, FALSE, 'Facebook', 'Mobile', 'Canada', 0.00), +('sess_003', 1003, '2024-01-16', 12, 780, 0.00, TRUE, 'Direct', 'Desktop', 'UK', 599.99), +('sess_004', 1004, '2024-01-16', 5, 230, 0.00, FALSE, 'Google', 'Tablet', 'Germany', 0.00), +('sess_005', 1001, '2024-01-17', 15, 920, 0.00, TRUE, 'Google', 'Desktop', 'USA', 1299.99), +('sess_006', 1005, '2024-01-17', 6, 340, 0.00, TRUE, 'Bing', 'Mobile', 'Australia', 199.99), +('sess_007', 1006, '2024-01-18', 2, 45, 1.00, FALSE, 'Facebook', 'Mobile', 'Brazil', 0.00), +('sess_008', 1007, '2024-01-18', 9, 560, 0.00, TRUE, 'Direct', 'Desktop', 'Japan', 799.99), +('sess_009', 1008, '2024-01-19', 4, 180, 0.50, FALSE, 'Google', 'Mobile', 'India', 0.00), +('sess_010', 1002, '2024-01-19', 11, 650, 0.00, TRUE, 'Direct', 'Desktop', 'Canada', 399.99); + +-- 插入员工绩效数据 +INSERT INTO employee_performance VALUES +(1001, '张工程师', '技术部', '高级工程师', '2024Q1', 8.5, 3, 45, 4.2, 8000.00), +(1001, '张工程师', '技术部', '高级工程师', '2023Q4', 9.2, 4, 52, 4.5, 12000.00), +(1002, '李架构师', '技术部', '系统架构师', '2024Q1', 9.0, 2, 38, 4.8, 15000.00), +(1002, '李架构师', '技术部', '系统架构师', '2023Q4', 8.8, 3, 42, 4.6, 13000.00), +(1003, '王产品', '产品部', '产品经理', '2024Q1', 7.8, 5, 35, 4.0, 6000.00), +(1003, '王产品', '产品部', '产品经理', '2023Q4', 8.2, 4, 28, 4.3, 7500.00), +(1004, '赵销售', '销售部', '销售经理', '2024Q1', 9.5, 8, 60, 4.7, 18000.00), +(1004, '赵销售', '销售部', '销售经理', '2023Q4', 9.1, 6, 55, 4.4, 16000.00), +(1005, '钱测试', '技术部', '测试工程师', '2024Q1', 8.0, 4, 40, 3.9, 5000.00), +(1005, '钱测试', '技术部', '测试工程师', '2023Q4', 7.5, 3, 35, 3.7, 4000.00), +(1006, '孙设计', '产品部', 'UI设计师', '2024Q1', 8.7, 6, 25, 4.1, 7000.00); +``` + +## 基础 HAVING 查询 + +### 1. 简单聚合函数过滤 + +```sql +-- 查找订单数量超过1个的销售代表 +SELECT sales_rep, + COUNT(*) as order_count, + ROUND(SUM(amount), 2) as total_sales +FROM sales_performance +GROUP BY sales_rep +HAVING COUNT(*) > 1 +ORDER BY total_sales DESC; +``` + +**结果:** +``` ++----------+-------------+-------------+ +| sales_rep | order_count | total_sales | ++----------+-------------+-------------+ +| 王强 | 3 | 49000.00 | +| 张明 | 3 | 48000.00 | +| 李华 | 3 | 39000.00 | +| 赵丽 | 2 | 20000.00 | ++----------+-------------+-------------+ +``` + +### 2. 平均值过滤 + +```sql +-- 查找平均成绩大于85分的学生 +SELECT student_name, + COUNT(*) as course_count, + ROUND(AVG(score), 2) as avg_score, + ROUND(SUM(credits * score) / SUM(credits), 2) as weighted_avg +FROM course_enrollments +GROUP BY student_id, student_name +HAVING AVG(score) > 85 +ORDER BY avg_score DESC; +``` + +**结果:** +``` ++-----------+--------------+-----------+-------------+ +| student_name | course_count | avg_score | weighted_avg | ++-----------+--------------+-----------+-------------+ +| 钱小伟 | 3 | 89.50 | 89.45 | +| 李小红 | 3 | 85.83 | 85.95 | ++-----------+--------------+-----------+-------------+ +``` + +### 3. 最值过滤 + +```sql +-- 查找最高销售额超过20000的地区 +SELECT region, + COUNT(*) as order_count, + MAX(amount) as max_sale, + MIN(amount) as min_sale, + ROUND(AVG(amount), 2) as avg_sale +FROM sales_performance +GROUP BY region +HAVING MAX(amount) > 20000 +ORDER BY max_sale DESC; +``` + +**结果:** +``` ++--------+-------------+----------+----------+----------+ +| region | order_count | max_sale | min_sale | avg_sale | ++--------+-------------+----------+----------+----------+ +| 华北 | 5 | 25000.00 | 8000.00 | 16400.00 | +| 华南 | 3 | 22000.00 | 11000.00 | 16333.33 | ++--------+-------------+----------+----------+----------+ +``` + +## 复杂 HAVING 条件 + +### 1. 多条件组合 + +```sql +-- 查找课程数量>=3且平均分>=80且总学分>=8的学生 +SELECT student_name, + COUNT(*) as course_count, + ROUND(AVG(score), 2) as avg_score, + SUM(credits) as total_credits, + GROUP_CONCAT(course_name ORDER BY score DESC) as courses +FROM course_enrollments +GROUP BY student_id, student_name +HAVING COUNT(*) >= 3 + AND AVG(score) >= 80 + AND SUM(credits) >= 8 +ORDER BY avg_score DESC; +``` + +**结果:** +``` ++-----------+--------------+-----------+---------------+--------------------------------------------------+ +| student_name | course_count | avg_score | total_credits | courses | ++-----------+--------------+-----------+---------------+--------------------------------------------------+ +| 钱小伟 | 3 | 89.50 | 8 | 程序设计,线性代数,英语写作 | +| 李小红 | 3 | 85.83 | 10 | 计算机科学导论,高等数学,大学物理 | +| 张小明 | 3 | 84.00 | 9 | 大学英语,计算机科学导论,高等数学 | +| 赵小丽 | 3 | 88.33 | 9 | 线性代数,计算机科学导论,大学物理 | ++-----------+--------------+-----------+---------------+--------------------------------------------------+ +``` + +### 2. 范围条件 + +```sql +-- 查找平均页面浏览量在5-10之间的流量来源 +SELECT traffic_source, + COUNT(*) as session_count, + ROUND(AVG(page_views), 2) as avg_page_views, + ROUND(AVG(session_duration), 2) as avg_duration, + SUM(conversion_flag) as conversions +FROM website_analytics +GROUP BY traffic_source +HAVING AVG(page_views) BETWEEN 5 AND 10 +ORDER BY avg_page_views DESC; +``` + +**结果:** +``` ++---------------+---------------+-----------------+--------------+-------------+ +| traffic_source | session_count | avg_page_views | avg_duration | conversions | ++---------------+---------------+-----------------+--------------+-------------+ +| Direct | 3 | 7.33 | 428.33 | 2 | +| Google | 4 | 7.00 | 372.50 | 2 | ++---------------+---------------+-----------------+--------------+-------------+ +``` + +### 3. 百分比和比率条件 + +```sql +-- 查找转化率大于50%的设备类型 +SELECT device_category, + COUNT(*) as total_sessions, + SUM(conversion_flag) as conversions, + ROUND(SUM(conversion_flag) / COUNT(*) * 100, 2) as conversion_rate, + ROUND(AVG(revenue), 2) as avg_revenue +FROM website_analytics +GROUP BY device_category +HAVING (SUM(conversion_flag) / COUNT(*) * 100) > 50 +ORDER BY conversion_rate DESC; +``` + +**结果:** +``` ++-----------------+----------------+-------------+-----------------+-------------+ +| device_category | total_sessions | conversions | conversion_rate | avg_revenue | ++-----------------+----------------+-------------+-----------------+-------------+ +| Desktop | 5 | 4 | 80.00 | 619.99 | ++-----------------+----------------+-------------+-----------------+-------------+ +``` + +## HAVING 与聚合函数组合 + +### 1. COUNT 相关条件 + +```sql +-- 多维度计数条件 +SELECT department, + quarter, + COUNT(*) as emp_count, + COUNT(CASE WHEN kpi_score >= 9.0 THEN 1 END) as excellent_count, + COUNT(CASE WHEN overtime_hours > 50 THEN 1 END) as overtime_count, + ROUND(AVG(kpi_score), 2) as avg_kpi +FROM employee_performance +GROUP BY department, quarter +HAVING COUNT(*) >= 2 -- 至少2名员工 + AND COUNT(CASE WHEN kpi_score >= 9.0 THEN 1 END) > 0 -- 至少1名优秀员工 +ORDER BY department, quarter; +``` + +**结果:** +``` ++----------+---------+-----------+-----------------+----------------+---------+ +| department | quarter | emp_count | excellent_count | overtime_count | avg_kpi | ++----------+---------+-----------+-----------------+----------------+---------+ +| 技术部 | 2023Q4 | 2 | 1 | 2 | 9.00 | +| 技术部 | 2024Q1 | 2 | 1 | 2 | 8.25 | ++----------+---------+-----------+-----------------+----------------+---------+ +``` + +### 2. SUM 和计算字段 + +```sql +-- 基于计算字段的过滤 +SELECT customer_segment, + state, + COUNT(*) as order_count, + SUM(quantity) as total_quantity, + ROUND(SUM(unit_price * quantity * (1 - discount)), 2) as revenue, + ROUND(SUM(profit), 2) as total_profit, + ROUND(SUM(profit) / SUM(unit_price * quantity * (1 - discount)) * 100, 2) as profit_margin +FROM ecommerce_orders +GROUP BY customer_segment, state +HAVING SUM(unit_price * quantity * (1 - discount)) > 1000 -- 收入超过1000 + AND SUM(profit) / SUM(unit_price * quantity * (1 - discount)) > 0.15 -- 利润率超过15% +ORDER BY profit_margin DESC; +``` + +**结果:** +``` ++------------------+------------+-------------+----------------+---------+-------------+--------------+ +| customer_segment | state | order_count | total_quantity | revenue | total_profit | profit_margin | ++------------------+------------+-------------+----------------+---------+-------------+--------------+ +| Home Office | Texas | 2 | 21 | 1559.79 | 300.00 | 19.23 | +| Consumer | Florida | 2 | 3 | 1499.99 | 450.00 | 30.00 | ++------------------+------------+-------------+----------------+---------+-------------+--------------+ +``` + +### 3. 复杂聚合条件 + +```sql +-- 组合多种聚合函数的复杂条件 +SELECT product_category, + customer_type, + COUNT(*) as sale_count, + ROUND(AVG(amount), 2) as avg_amount, + ROUND(STD(amount), 2) as amount_std, + MIN(amount) as min_amount, + MAX(amount) as max_amount, + ROUND(SUM(amount * commission_rate), 2) as total_commission +FROM sales_performance +GROUP BY product_category, customer_type +HAVING COUNT(*) >= 2 -- 至少2笔销售 + AND AVG(amount) > 10000 -- 平均金额超过10000 + AND (MAX(amount) - MIN(amount)) > 5000 -- 金额差异超过5000 + AND STD(amount) < 8000 -- 标准差小于8000(相对稳定) +ORDER BY avg_amount DESC; +``` + +**结果:** +``` ++------------------+---------------+------------+------------+------------+------------+------------+------------------+ +| product_category | customer_type | sale_count | avg_amount | amount_std | min_amount | max_amount | total_commission | ++------------------+---------------+------------+------------+------------+------------+------------+------------------+ +| 电子产品 | VIP客户 | 2 | 21500.00 | 3500.00 | 18000.00 | 25000.00 | 1620.00 | +| 家电 | VIP客户 | 2 | 16500.00 | 7778.17 | 11000.00 | 22000.00 | 1320.00 | ++------------------+---------------+------------+------------+------------+------------+------------+------------------+ +``` + +## 多层嵌套和子查询 + +### 1. HAVING 中使用子查询 + +```sql +-- 查找销售额超过平均水平的销售代表 +SELECT sales_rep, + COUNT(*) as order_count, + ROUND(SUM(amount), 2) as total_sales, + ROUND(AVG(amount), 2) as avg_order_value +FROM sales_performance +GROUP BY sales_rep +HAVING SUM(amount) > ( + SELECT AVG(total_sales) + FROM ( + SELECT SUM(amount) as total_sales + FROM sales_performance + GROUP BY sales_rep + ) as rep_totals +) +ORDER BY total_sales DESC; +``` + +**结果:** +``` ++----------+-------------+-------------+-----------------+ +| sales_rep | order_count | total_sales | avg_order_value | ++----------+-------------+-------------+-----------------+ +| 王强 | 3 | 49000.00 | 16333.33 | +| 张明 | 3 | 48000.00 | 16000.00 | +| 李华 | 3 | 39000.00 | 13000.00 | ++----------+-------------+-------------+-----------------+ +``` + +### 2. 嵌套分组查询 + +```sql +-- 查找在多个系别都有课程且平均分都超过80的教师 +SELECT teacher, + COUNT(DISTINCT department) as dept_count, + COUNT(*) as course_count, + ROUND(AVG(score), 2) as overall_avg_score, + GROUP_CONCAT(DISTINCT department) as departments +FROM course_enrollments +GROUP BY teacher +HAVING COUNT(DISTINCT department) > 1 + AND teacher IN ( + SELECT teacher + FROM course_enrollments + GROUP BY teacher, department + HAVING AVG(score) > 80 + ) +ORDER BY overall_avg_score DESC; +``` + +### 3. 窗口函数与 HAVING 结合 + +```sql +-- 查找在部门内排名前50%的员工组成的部门(平均绩效高的部门) +WITH ranked_employees AS ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY department ORDER BY kpi_score DESC) as dept_rank, + COUNT(*) OVER (PARTITION BY department) as dept_size + FROM employee_performance + WHERE quarter = '2024Q1' +), +top_performers AS ( + SELECT * + FROM ranked_employees + WHERE dept_rank <= dept_size / 2 +) +SELECT department, + COUNT(*) as top_performer_count, + ROUND(AVG(kpi_score), 2) as avg_top_kpi, + ROUND(AVG(bonus), 2) as avg_top_bonus +FROM top_performers +GROUP BY department +HAVING AVG(kpi_score) > 8.5 +ORDER BY avg_top_kpi DESC; +``` + +## 面试题和实际案例 + +### 面试题1:连续增长分析 + +**题目**:找出连续两个季度绩效都在提升的员工。 + +```sql +-- 解答:使用自连接和HAVING +SELECT e1.name, + e1.department, + e1.quarter as current_quarter, + e1.kpi_score as current_kpi, + e2.quarter as prev_quarter, + e2.kpi_score as prev_kpi, + ROUND(e1.kpi_score - e2.kpi_score, 2) as improvement +FROM employee_performance e1 +JOIN employee_performance e2 ON e1.emp_id = e2.emp_id +WHERE e1.quarter = '2024Q1' + AND e2.quarter = '2023Q4' + AND e1.kpi_score > e2.kpi_score +GROUP BY e1.emp_id, e1.name, e1.department, e1.quarter, e1.kpi_score, e2.quarter, e2.kpi_score +HAVING e1.kpi_score > e2.kpi_score +ORDER BY improvement DESC; +``` + +**结果:** +``` ++----------+----------+-----------------+-------------+--------------+----------+-------------+ +| name | department | current_quarter | current_kpi | prev_quarter | prev_kpi | improvement | ++----------+----------+-----------------+-------------+--------------+----------+-------------+ +| 钱测试 | 技术部 | 2024Q1 | 8.0 | 2023Q4 | 7.5 | 0.50 | +| 赵销售 | 销售部 | 2024Q1 | 9.5 | 2023Q4 | 9.1 | 0.40 | ++----------+----------+-----------------+-------------+--------------+----------+-------------+ +``` + +### 面试题2:帕累托分析(80/20原则) + +**题目**:找出贡献了80%收入的前20%客户群体。 + +```sql +-- 解答:使用累计计算和HAVING +WITH customer_revenue AS ( + SELECT customer_segment, + SUM(unit_price * quantity * (1 - discount)) as total_revenue + FROM ecommerce_orders + GROUP BY customer_segment +), +revenue_ranking AS ( + SELECT customer_segment, + total_revenue, + SUM(total_revenue) OVER() as grand_total, + SUM(total_revenue) OVER(ORDER BY total_revenue DESC) as cumulative_revenue + FROM customer_revenue +), +pareto_analysis AS ( + SELECT customer_segment, + total_revenue, + ROUND(total_revenue / grand_total * 100, 2) as revenue_percentage, + ROUND(cumulative_revenue / grand_total * 100, 2) as cumulative_percentage + FROM revenue_ranking +) +SELECT customer_segment, + total_revenue, + revenue_percentage, + cumulative_percentage +FROM pareto_analysis +GROUP BY customer_segment, total_revenue, revenue_percentage, cumulative_percentage +HAVING cumulative_percentage <= 80 +ORDER BY total_revenue DESC; +``` + +### 面试题3:同期对比分析 + +**题目**:比较每个产品类别在不同客户类型中的表现差异。 + +```sql +-- 解答:使用条件聚合和HAVING +SELECT product_category, + COUNT(*) as total_sales, + SUM(CASE WHEN customer_type = 'VIP客户' THEN amount ELSE 0 END) as vip_sales, + SUM(CASE WHEN customer_type = '新客户' THEN amount ELSE 0 END) as new_customer_sales, + SUM(CASE WHEN customer_type = '老客户' THEN amount ELSE 0 END) as old_customer_sales, + ROUND( + SUM(CASE WHEN customer_type = 'VIP客户' THEN amount ELSE 0 END) / + SUM(amount) * 100, 2 + ) as vip_percentage +FROM sales_performance +GROUP BY product_category +HAVING COUNT(DISTINCT customer_type) >= 2 -- 至少有2种客户类型 + AND SUM(CASE WHEN customer_type = 'VIP客户' THEN amount ELSE 0 END) > + SUM(CASE WHEN customer_type = '新客户' THEN amount ELSE 0 END) -- VIP销售额超过新客户 +ORDER BY vip_percentage DESC; +``` + +**结果:** +``` ++------------------+-------------+-----------+--------------------+--------------------+----------------+ +| product_category | total_sales | vip_sales | new_customer_sales | old_customer_sales | vip_percentage | ++------------------+-------------+-----------+--------------------+--------------------+----------------+ +| 电子产品 | 5 | 43000.00 | 35000.00 | 16000.00 | 45.74 | +| 家电 | 3 | 22000.00 | 0.00 | 25000.00 | 46.81 | ++------------------+-------------+-----------+--------------------+--------------------+----------------+ +``` + +### 实际案例1:电商业务分析 + +**场景**:电商平台需要识别高价值客户群体和优质产品类别。 + +```sql +-- 综合业务分析:识别高价值客户群体 +SELECT customer_segment, + region, + COUNT(*) as order_count, + COUNT(DISTINCT customer_id) as unique_customers, + ROUND(AVG(unit_price * quantity * (1 - discount)), 2) as avg_order_value, + SUM(profit) as total_profit, + ROUND(SUM(profit) / SUM(unit_price * quantity * (1 - discount)) * 100, 2) as profit_margin, + ROUND(COUNT(*) / COUNT(DISTINCT customer_id), 2) as orders_per_customer +FROM ecommerce_orders +GROUP BY customer_segment, region +HAVING COUNT(*) >= 2 -- 至少2个订单 + AND COUNT(DISTINCT customer_id) >= 1 -- 至少1个客户 + AND AVG(unit_price * quantity * (1 - discount)) > 200 -- 平均订单价值超过200 + AND SUM(profit) / SUM(unit_price * quantity * (1 - discount)) > 0.20 -- 利润率超过20% +ORDER BY profit_margin DESC, avg_order_value DESC; +``` + +### 实际案例2:教育数据分析 + +**场景**:教务处需要分析课程质量和学生表现。 + +```sql +-- 课程质量分析:识别优质课程和问题课程 +SELECT course_name, + teacher, + department, + COUNT(*) as student_count, + ROUND(AVG(score), 2) as avg_score, + ROUND(STD(score), 2) as score_std, + MIN(score) as min_score, + MAX(score) as max_score, + COUNT(CASE WHEN score >= 90 THEN 1 END) as excellent_count, + COUNT(CASE WHEN score < 70 THEN 1 END) as poor_count +FROM course_enrollments +GROUP BY course_id, course_name, teacher, department +HAVING COUNT(*) >= 3 -- 至少3名学生 + AND AVG(score) >= 80 -- 平均分不低于80 + AND STD(score) <= 10 -- 分数差异不过大 + AND COUNT(CASE WHEN score < 70 THEN 1 END) = 0 -- 没有不及格学生 +ORDER BY avg_score DESC, score_std ASC; +``` + +**结果:** +``` ++----------------+----------+----------+---------------+-----------+-----------+-----------+-----------+-----------------+------------+ +| course_name | teacher | department | student_count | avg_score | score_std | min_score | max_score | excellent_count | poor_count | ++----------------+----------+----------+---------------+-----------+-----------+-----------+-----------+-----------------+------------+ +| 计算机科学导论 | 王教授 | 计算机系 | 3 | 88.83 | 3.64 | 85.5 | 92.0 | 1 | 0 | +| 线性代数 | 孙教授 | 数学系 | 2 | 89.75 | 2.47 | 88.0 | 91.5 | 1 | 0 | ++----------------+----------+----------+---------------+-----------+-----------+-----------+-----------+-----------------+------------+ +``` + +### 实际案例3:网站运营优化 + +**场景**:网站运营团队需要优化流量渠道和用户体验。 + +```sql +-- 流量渠道效果分析 +SELECT traffic_source, + device_category, + COUNT(*) as session_count, + ROUND(AVG(page_views), 2) as avg_page_views, + ROUND(AVG(session_duration/60), 2) as avg_minutes, + SUM(conversion_flag) as conversions, + ROUND(SUM(conversion_flag)/COUNT(*)*100, 2) as conversion_rate, + ROUND(SUM(revenue), 2) as total_revenue, + ROUND(SUM(revenue)/COUNT(*), 2) as revenue_per_session +FROM website_analytics +GROUP BY traffic_source, device_category +HAVING COUNT(*) >= 2 -- 足够的样本量 + AND AVG(session_duration) >= 180 -- 平均会话时长超过3分钟 + AND SUM(conversion_flag)/COUNT(*) >= 0.3 -- 转化率至少30% +ORDER BY conversion_rate DESC, revenue_per_session DESC; +``` + +**结果:** +``` ++----------------+-----------------+---------------+-----------------+-------------+-------------+-----------------+---------------+--------------------+ +| traffic_source | device_category | session_count | avg_page_views | avg_minutes | conversions | conversion_rate | total_revenue | revenue_per_session | ++----------------+-----------------+---------------+-----------------+-------------+-------------+-----------------+---------------+--------------------+ +| Google | Desktop | 2 | 11.50 | 7.58 | 2 | 100.00 | 1599.98 | 799.99 | +| Direct | Desktop | 3 | 7.33 | 7.14 | 2 | 66.67 | 1599.98 | 533.33 | ++----------------+-----------------+---------------+-----------------+-------------+-------------+-----------------+---------------+--------------------+ +``` + +## 性能优化和最佳实践 + +### 1. 索引优化 + +```sql +-- 为GROUP BY和HAVING中使用的列创建索引 +CREATE INDEX idx_sales_performance_rep_region ON sales_performance(sales_rep, region); +CREATE INDEX idx_course_enrollments_student ON course_enrollments(student_id, student_name); +CREATE INDEX idx_ecommerce_orders_segment_state ON ecommerce_orders(customer_segment, state); + +-- 为聚合计算创建覆盖索引 +CREATE INDEX idx_sales_performance_covering ON sales_performance(sales_rep, amount, quantity, customer_type); +``` + +### 2. 查询优化策略 + +```sql +-- ✅ 推荐:先用WHERE过滤,减少分组数据量 +SELECT department, AVG(salary) +FROM employees +WHERE hire_date >= '2020-01-01' -- 先过滤 +GROUP BY department +HAVING AVG(salary) > 15000; + +-- ❌ 避免:直接分组后过滤 +SELECT department, AVG(salary) +FROM employees +GROUP BY department +HAVING AVG(salary) > 15000 AND MIN(hire_date) >= '2020-01-01'; +``` + +### 3. 避免复杂HAVING条件 + +```sql +-- ❌ 复杂的HAVING条件 +SELECT sales_rep, + COUNT(*) as order_count, + SUM(amount) as total_sales +FROM sales_performance +GROUP BY sales_rep +HAVING SUM(amount) > (SELECT AVG(total) FROM (SELECT SUM(amount) as total FROM sales_performance GROUP BY sales_rep) t) + AND COUNT(*) > (SELECT AVG(cnt) FROM (SELECT COUNT(*) as cnt FROM sales_performance GROUP BY sales_rep) t); + +-- ✅ 优化:分步处理 +WITH rep_stats AS ( + SELECT sales_rep, + COUNT(*) as order_count, + SUM(amount) as total_sales + FROM sales_performance + GROUP BY sales_rep +), +benchmarks AS ( + SELECT AVG(total_sales) as avg_sales, + AVG(order_count) as avg_orders + FROM rep_stats +) +SELECT rs.sales_rep, rs.order_count, rs.total_sales +FROM rep_stats rs, benchmarks b +WHERE rs.total_sales > b.avg_sales + AND rs.order_count > b.avg_orders; +``` + +### 4. 内存优化 + +```sql +-- 优化GROUP BY相关的内存设置 +SET SESSION group_concat_max_len = 10240; +SET SESSION tmp_table_size = 134217728; -- 128MB +SET SESSION max_heap_table_size = 134217728; -- 128MB + +-- 对于大数据量,考虑分页处理 +SELECT sales_rep, COUNT(*), SUM(amount) +FROM sales_performance +WHERE sale_date BETWEEN '2024-01-01' AND '2024-01-31' -- 限制时间范围 +GROUP BY sales_rep +HAVING COUNT(*) > 5 +ORDER BY SUM(amount) DESC +LIMIT 20; -- 限制结果数量 +``` + +### 5. 监控和调优 + +```sql +-- 查看HAVING相关查询的执行计划 +EXPLAIN FORMAT=JSON +SELECT department, COUNT(*), AVG(salary) +FROM employees +GROUP BY department +HAVING COUNT(*) > 3; + +-- 监控分组查询性能 +SELECT + SUBSTRING(sql_text, 1, 100) as query_start, + exec_count, + avg_timer_wait/1000000000000 as avg_time_sec, + sum_rows_examined/exec_count as avg_rows_examined +FROM performance_schema.events_statements_summary_by_digest +WHERE sql_text LIKE '%HAVING%' +ORDER BY avg_timer_wait DESC +LIMIT 10; +``` + +### 6. 最佳实践总结 + +```sql +-- ✅ HAVING 最佳实践 +SELECT + category, -- 明确的分组字段 + COUNT(*) as product_count, -- 清晰的聚合函数 + ROUND(AVG(price), 2) as avg_price -- 合理的精度 +FROM products +WHERE status = 'active' -- 先过滤原始数据 +GROUP BY category -- 简单的分组 +HAVING COUNT(*) >= 5 -- 简单明确的HAVING条件 + AND AVG(price) > 100 -- 避免过复杂的条件 +ORDER BY avg_price DESC -- 合理排序 +LIMIT 20; -- 限制结果数量 + +-- ❌ 避免的问题 +-- 1. HAVING中使用非聚合函数(应该用WHERE) +-- 2. 过于复杂的HAVING条件 +-- 3. 在HAVING中进行大量计算 +-- 4. 不必要的子查询在HAVING中 +-- 5. 缺少适当的索引支持 +``` + +--- + +**总结:** +- HAVING 专门用于过滤 GROUP BY 分组后的结果 +- 理解与 WHERE 的区别:时机、功能、性能 +- 可以使用聚合函数进行复杂的条件判断 +- 合理使用索引和分步查询优化性能 +- 避免过于复杂的 HAVING 条件 +- 结合实际业务场景灵活运用 \ No newline at end of file diff --git a/MySQL_ORDER_BY语法使用文档.md b/MySQL_ORDER_BY语法使用文档.md new file mode 100644 index 0000000..91f8ae2 --- /dev/null +++ b/MySQL_ORDER_BY语法使用文档.md @@ -0,0 +1,769 @@ +# MySQL ORDER BY 语法使用文档 + +## 目录 +1. [ORDER BY 基础语法](#order-by-基础语法) +2. [ORDER BY 规则和特性](#order-by-规则和特性) +3. [示例数据准备](#示例数据准备) +4. [基础排序示例](#基础排序示例) +5. [多列排序](#多列排序) +6. [特殊排序场景](#特殊排序场景) +7. [复杂排序查询](#复杂排序查询) +8. [面试题和实际案例](#面试题和实际案例) +9. [性能优化和最佳实践](#性能优化和最佳实践) + +## ORDER BY 基础语法 + +ORDER BY 用于对查询结果进行排序。 + +```sql +SELECT column1, column2, ... +FROM table_name +[WHERE condition] +ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ... +[LIMIT number]; +``` + +**关键字说明:** +- `ASC`:升序排列(默认) +- `DESC`:降序排列 +- 可以指定多个排序列 +- NULL 值默认排在最前面(ASC)或最后面(DESC) + +## ORDER BY 规则和特性 + +1. **排序优先级**:从左到右依次排序 +2. **数据类型排序**: + - 数字:按数值大小 + - 字符串:按字典序(字母顺序) + - 日期:按时间先后 + - NULL:默认最小值处理 +3. **性能影响**:ORDER BY 可能触发文件排序,影响查询性能 +4. **与 LIMIT 结合**:获取排序后的前 N 条记录 + +## 示例数据准备 + +### 创建示例表 + +```sql +-- 学生表 +CREATE TABLE students ( + id INT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(50) NOT NULL, + age INT, + gender ENUM('男', '女'), + class_id INT, + score DECIMAL(5,2), + enrollment_date DATE, + city VARCHAR(30) +); + +-- 订单表 +CREATE TABLE orders ( + order_id INT PRIMARY KEY AUTO_INCREMENT, + customer_name VARCHAR(50), + product_name VARCHAR(100), + order_date DATETIME, + amount DECIMAL(10,2), + status ENUM('pending', 'processing', 'completed', 'cancelled'), + priority INT, + region VARCHAR(30) +); + +-- 员工表 +CREATE TABLE staff ( + emp_id INT PRIMARY KEY, + name VARCHAR(50), + department VARCHAR(30), + salary DECIMAL(10,2), + hire_date DATE, + manager_id INT, + performance_score DECIMAL(3,1), + bonus DECIMAL(8,2) +); + +-- 产品销售表 +CREATE TABLE product_sales ( + id INT PRIMARY KEY AUTO_INCREMENT, + product_id INT, + product_name VARCHAR(100), + category VARCHAR(50), + sale_date DATE, + quantity INT, + unit_price DECIMAL(8,2), + discount_rate DECIMAL(3,2), + sales_rep VARCHAR(50) +); +``` + +### 插入示例数据 + +```sql +-- 插入学生数据 +INSERT INTO students (name, age, gender, class_id, score, enrollment_date, city) VALUES +('张三', 20, '男', 1, 85.5, '2023-09-01', '北京'), +('李四', 19, '女', 1, 92.0, '2023-09-01', '上海'), +('王五', 21, '男', 2, 78.5, '2023-09-01', '深圳'), +('赵六', 20, '女', 2, 88.0, '2023-09-01', '广州'), +('钱七', 22, '男', 1, 76.0, '2022-09-01', '北京'), +('孙八', 19, '女', 3, 95.5, '2023-09-01', '杭州'), +('周九', 20, '男', 3, 82.0, '2023-09-01', '成都'), +('吴十', 21, '女', 2, 89.5, '2022-09-01', '南京'), +('郑一', 19, '男', 1, 91.0, '2023-09-01', '西安'), +('王二', NULL, '女', 3, 87.0, '2023-09-01', '重庆'); + +-- 插入订单数据 +INSERT INTO orders (customer_name, product_name, order_date, amount, status, priority, region) VALUES +('客户A', '笔记本电脑', '2024-01-15 10:30:00', 5999.00, 'completed', 1, '华北'), +('客户B', '手机', '2024-01-16 14:20:00', 3999.00, 'processing', 2, '华东'), +('客户C', '平板电脑', '2024-01-16 16:45:00', 2999.00, 'pending', 3, '华南'), +('客户A', '耳机', '2024-01-17 09:15:00', 299.00, 'completed', 2, '华北'), +('客户D', '键盘', '2024-01-17 11:30:00', 199.00, 'cancelled', 3, '华西'), +('客户E', '鼠标', '2024-01-18 13:40:00', 99.00, 'completed', 1, '华东'), +('客户B', '显示器', '2024-01-18 15:20:00', 1299.00, 'processing', 1, '华东'), +('客户F', '音响', '2024-01-19 08:50:00', 899.00, 'pending', 2, '华南'), +('客户C', '摄像头', '2024-01-19 12:10:00', 399.00, 'completed', 3, '华南'), +('客户G', '打印机', '2024-01-20 16:30:00', 1599.00, 'processing', 1, '华北'); + +-- 插入员工数据 +INSERT INTO staff (emp_id, name, department, salary, hire_date, manager_id, performance_score, bonus) VALUES +(1001, '张经理', '技术部', 15000.00, '2020-03-15', NULL, 9.2, 5000.00), +(1002, '李工程师', '技术部', 12000.00, '2021-05-20', 1001, 8.8, 3000.00), +(1003, '王设计师', '设计部', 10000.00, '2021-08-10', NULL, 8.5, 2500.00), +(1004, '赵分析师', '数据部', 11000.00, '2022-01-15', NULL, 9.0, 3500.00), +(1005, '钱开发', '技术部', 9000.00, '2022-04-20', 1001, 7.8, 2000.00), +(1006, '孙测试', '技术部', 8500.00, '2022-07-01', 1001, 8.2, 1500.00), +(1007, '周产品', '产品部', 13000.00, '2021-12-05', NULL, 8.9, 4000.00), +(1008, '吴运营', '运营部', 8000.00, '2023-02-10', NULL, 7.5, 1000.00), +(1009, '郑销售', '销售部', 7500.00, '2023-03-20', NULL, 8.1, 1800.00), +(1010, '刘助理', '技术部', 6000.00, '2023-09-01', 1002, NULL, 500.00); + +-- 插入产品销售数据 +INSERT INTO product_sales (product_id, product_name, category, sale_date, quantity, unit_price, discount_rate, sales_rep) VALUES +(101, 'iPhone 15', '手机', '2024-01-10', 50, 5999.00, 0.05, '张销售'), +(102, '华为Mate60', '手机', '2024-01-10', 30, 4999.00, 0.10, '李销售'), +(103, 'MacBook Pro', '笔记本', '2024-01-11', 20, 12999.00, 0.03, '张销售'), +(104, '联想ThinkPad', '笔记本', '2024-01-11', 40, 6999.00, 0.08, '王销售'), +(105, 'iPad Air', '平板', '2024-01-12', 25, 3999.00, 0.06, '李销售'), +(106, '小米平板', '平板', '2024-01-12', 35, 1999.00, 0.12, '赵销售'), +(107, 'AirPods Pro', '耳机', '2024-01-13', 100, 1999.00, 0.08, '张销售'), +(108, '索尼耳机', '耳机', '2024-01-13', 60, 2999.00, 0.15, '王销售'), +(109, 'Dell显示器', '显示器', '2024-01-14', 80, 1599.00, 0.10, '李销售'), +(110, '三星显示器', '显示器', '2024-01-14', 45, 2299.00, 0.07, '赵销售'); +``` + +## 基础排序示例 + +### 1. 单列升序排序(默认) + +```sql +-- 按年龄升序排列学生 +SELECT name, age FROM students ORDER BY age; +``` + +**结果:** +``` ++------+------+ +| name | age | ++------+------+ +| 王二 | NULL | +| 李四 | 19 | +| 孙八 | 19 | +| 郑一 | 19 | +| 张三 | 20 | +| 赵六 | 20 | +| 周九 | 20 | +| 王五 | 21 | +| 吴十 | 21 | +| 钱七 | 22 | ++------+------+ +``` + +### 2. 单列降序排序 + +```sql +-- 按分数降序排列学生 +SELECT name, score FROM students ORDER BY score DESC; +``` + +**结果:** +``` ++------+-------+ +| name | score | ++------+-------+ +| 孙八 | 95.50 | +| 李四 | 92.00 | +| 郑一 | 91.00 | +| 吴十 | 89.50 | +| 赵六 | 88.00 | +| 王二 | 87.00 | +| 张三 | 85.50 | +| 周九 | 82.00 | +| 王五 | 78.50 | +| 钱七 | 76.00 | ++------+-------+ +``` + +### 3. 字符串排序 + +```sql +-- 按姓名字母顺序排列 +SELECT name, age FROM students ORDER BY name; +``` + +**结果:** +``` ++------+------+ +| name | age | ++------+------+ +| 钱七 | 22 | +| 孙八 | 19 | +| 王二 | NULL | +| 王五 | 21 | +| 吴十 | 21 | +| 张三 | 20 | +| 赵六 | 20 | +| 郑一 | 19 | +| 周九 | 20 | +| 李四 | 19 | ++------+-------+ +``` + +### 4. 日期排序 + +```sql +-- 按入学日期排序 +SELECT name, enrollment_date FROM students ORDER BY enrollment_date; +``` + +**结果:** +``` ++------+-----------------+ +| name | enrollment_date | ++------+-----------------+ +| 钱七 | 2022-09-01 | +| 吴十 | 2022-09-01 | +| 张三 | 2023-09-01 | +| 李四 | 2023-09-01 | +| 王五 | 2023-09-01 | +| 赵六 | 2023-09-01 | +| 孙八 | 2023-09-01 | +| 周九 | 2023-09-01 | +| 郑一 | 2023-09-01 | +| 王二 | 2023-09-01 | ++------+-----------------+ +``` + +## 多列排序 + +### 1. 多列升序排序 + +```sql +-- 先按班级排序,再按分数排序 +SELECT name, class_id, score +FROM students +ORDER BY class_id, score; +``` + +**结果:** +``` ++------+----------+-------+ +| name | class_id | score | ++------+----------+-------+ +| 钱七 | 1 | 76.00 | +| 张三 | 1 | 85.50 | +| 郑一 | 1 | 91.00 | +| 李四 | 1 | 92.00 | +| 王五 | 2 | 78.50 | +| 赵六 | 2 | 88.00 | +| 吴十 | 2 | 89.50 | +| 周九 | 3 | 82.00 | +| 王二 | 3 | 87.00 | +| 孙八 | 3 | 95.50 | ++------+----------+-------+ +``` + +### 2. 混合排序(升序+降序) + +```sql +-- 按班级升序,分数降序 +SELECT name, class_id, score +FROM students +ORDER BY class_id ASC, score DESC; +``` + +**结果:** +``` ++------+----------+-------+ +| name | class_id | score | ++------+----------+-------+ +| 李四 | 1 | 92.00 | +| 郑一 | 1 | 91.00 | +| 张三 | 1 | 85.50 | +| 钱七 | 1 | 76.00 | +| 吴十 | 2 | 89.50 | +| 赵六 | 2 | 88.00 | +| 王五 | 2 | 78.50 | +| 孙八 | 3 | 95.50 | +| 王二 | 3 | 87.00 | +| 周九 | 3 | 82.00 | ++------+----------+-------+ +``` + +### 3. 三列排序 + +```sql +-- 按部门、薪资、绩效排序 +SELECT name, department, salary, performance_score +FROM staff +ORDER BY department, salary DESC, performance_score DESC; +``` + +**结果:** +``` ++----------+----------+----------+------------------+ +| name | department | salary | performance_score | ++----------+----------+----------+------------------+ +| 王设计师 | 设计部 | 10000.00 | 8.50 | +| 赵分析师 | 数据部 | 11000.00 | 9.00 | +| 周产品 | 产品部 | 13000.00 | 8.90 | +| 张经理 | 技术部 | 15000.00 | 9.20 | +| 李工程师 | 技术部 | 12000.00 | 8.80 | +| 钱开发 | 技术部 | 9000.00 | 7.80 | +| 孙测试 | 技术部 | 8500.00 | 8.20 | +| 刘助理 | 技术部 | 6000.00 | NULL | +| 吴运营 | 运营部 | 8000.00 | 7.50 | +| 郑销售 | 销售部 | 7500.00 | 8.10 | ++----------+----------+----------+------------------+ +``` + +## 特殊排序场景 + +### 1. NULL 值处理 + +```sql +-- 查看NULL值的排序位置 +SELECT name, age FROM students ORDER BY age; + +-- 使用 ISNULL() 函数控制NULL值排序 +SELECT name, age +FROM students +ORDER BY ISNULL(age), age; -- NULL值排在最后 +``` + +### 2. 自定义排序(FIELD函数) + +```sql +-- 按自定义状态优先级排序 +SELECT customer_name, status, amount +FROM orders +ORDER BY FIELD(status, 'pending', 'processing', 'completed', 'cancelled'), amount DESC; +``` + +**结果:** +``` ++------------+------------+---------+ +| customer_name | status | amount | ++------------+------------+---------+ +| 客户C | pending | 2999.00 | +| 客户F | pending | 899.00 | +| 客户B | processing | 3999.00 | +| 客户G | processing | 1599.00 | +| 客户B | processing | 1299.00 | +| 客户A | completed | 5999.00 | +| 客户F | completed | 899.00 | +| 客户C | completed | 399.00 | +| 客户A | completed | 299.00 | +| 客户E | completed | 99.00 | +| 客户D | cancelled | 199.00 | ++------------+------------+---------+ +``` + +### 3. 条件排序(CASE WHEN) + +```sql +-- 根据不同条件进行排序 +SELECT name, department, salary, + CASE + WHEN department = '技术部' THEN 1 + WHEN department = '产品部' THEN 2 + WHEN department = '设计部' THEN 3 + ELSE 4 + END AS dept_priority +FROM staff +ORDER BY dept_priority, salary DESC; +``` + +### 4. 计算字段排序 + +```sql +-- 按销售额排序(数量×单价×(1-折扣率)) +SELECT product_name, quantity, unit_price, discount_rate, + ROUND(quantity * unit_price * (1 - discount_rate), 2) AS total_sales +FROM product_sales +ORDER BY total_sales DESC; +``` + +**结果:** +``` ++-------------+----------+------------+---------------+-------------+ +| product_name | quantity | unit_price | discount_rate | total_sales | ++-------------+----------+------------+---------------+-------------+ +| iPhone 15 | 50 | 5999.00 | 0.05 | 284952.50 | +| MacBook Pro | 20 | 12999.00 | 0.03 | 251980.60 | +| 联想ThinkPad | 40 | 6999.00 | 0.08 | 257569.60 | +| AirPods Pro | 100 | 1999.00 | 0.08 | 183908.00 | +| 索尼耳机 | 60 | 2999.00 | 0.15 | 152949.00 | +| 华为Mate60 | 30 | 4999.00 | 0.10 | 134973.00 | +| Dell显示器 | 80 | 1599.00 | 0.10 | 115128.00 | +| iPad Air | 25 | 3999.00 | 0.06 | 93975.00 | +| 三星显示器 | 45 | 2299.00 | 0.07 | 96218.55 | +| 小米平板 | 35 | 1999.00 | 0.12 | 61652.40 | ++-------------+----------+------------+---------------+-------------+ +``` + +## 复杂排序查询 + +### 1. 分组后排序 + +```sql +-- 各部门平均薪资,按平均薪资降序 +SELECT department, + COUNT(*) AS emp_count, + ROUND(AVG(salary), 2) AS avg_salary +FROM staff +GROUP BY department +ORDER BY avg_salary DESC; +``` + +**结果:** +``` ++----------+-----------+------------+ +| department | emp_count | avg_salary | ++----------+-----------+------------+ +| 技术部 | 5 | 10100.00 | +| 产品部 | 1 | 13000.00 | +| 数据部 | 1 | 11000.00 | +| 设计部 | 1 | 10000.00 | +| 运营部 | 1 | 8000.00 | +| 销售部 | 1 | 7500.00 | ++----------+-----------+------------+ +``` + +### 2. 子查询结果排序 + +```sql +-- 查找每个班级分数最高的学生 +SELECT s1.name, s1.class_id, s1.score +FROM students s1 +WHERE s1.score = ( + SELECT MAX(s2.score) + FROM students s2 + WHERE s2.class_id = s1.class_id +) +ORDER BY s1.class_id, s1.score DESC; +``` + +### 3. 窗口函数排序 + +```sql +-- 使用ROW_NUMBER()进行排名 +SELECT name, department, salary, + ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank, + ROW_NUMBER() OVER (ORDER BY salary DESC) AS overall_rank +FROM staff +ORDER BY department, dept_rank; +``` + +### 4. 复杂条件排序 + +```sql +-- 优先显示高优先级且金额大的订单 +SELECT customer_name, product_name, amount, priority, status, + CASE + WHEN priority = 1 AND amount > 1000 THEN 1 + WHEN priority = 1 THEN 2 + WHEN priority = 2 AND amount > 1000 THEN 3 + WHEN priority = 2 THEN 4 + ELSE 5 + END AS sort_priority +FROM orders +ORDER BY sort_priority, amount DESC; +``` + +## 面试题和实际案例 + +### 面试题1:销售排名问题 + +**题目**:查询每个销售代表的销售总额,并按销售额降序排列,同时显示排名。 + +```sql +-- 解答 +SELECT sales_rep, + COUNT(*) AS product_count, + SUM(quantity * unit_price * (1 - discount_rate)) AS total_sales, + RANK() OVER (ORDER BY SUM(quantity * unit_price * (1 - discount_rate)) DESC) AS sales_rank +FROM product_sales +GROUP BY sales_rep +ORDER BY total_sales DESC; +``` + +**结果:** +``` ++----------+---------------+-------------+------------+ +| sales_rep | product_count | total_sales | sales_rank | ++----------+---------------+-------------+------------+ +| 张销售 | 3 | 720841.10 | 1 | +| 李销售 | 3 | 344076.00 | 2 | +| 王销售 | 2 | 410518.20 | 3 | +| 赵销售 | 2 | 157870.95 | 4 | ++----------+---------------+-------------+------------+ +``` + +### 面试题2:分页查询优化 + +**题目**:实现高效的分页查询,按ID排序。 + +```sql +-- 传统分页(性能较差) +SELECT * FROM orders ORDER BY order_id LIMIT 1000000, 10; + +-- 优化后的分页(使用索引) +SELECT o.* FROM orders o +WHERE o.order_id > ( + SELECT order_id FROM orders ORDER BY order_id LIMIT 999999, 1 +) +ORDER BY o.order_id LIMIT 10; + +-- 更好的分页方案(记住上次的最后一条记录ID) +SELECT * FROM orders +WHERE order_id > 1000000 -- 上次查询的最后一个ID +ORDER BY order_id LIMIT 10; +``` + +### 面试题3:Top-N问题 + +**题目**:查询每个类别销售额前2名的产品。 + +```sql +-- 使用窗口函数解决 +SELECT category, product_name, total_sales, category_rank +FROM ( + SELECT category, product_name, + quantity * unit_price * (1 - discount_rate) AS total_sales, + ROW_NUMBER() OVER (PARTITION BY category ORDER BY quantity * unit_price * (1 - discount_rate) DESC) AS category_rank + FROM product_sales +) ranked_sales +WHERE category_rank <= 2 +ORDER BY category, category_rank; +``` + +### 实际案例1:电商订单分析 + +**场景**:电商平台需要分析订单数据,按多个维度排序。 + +```sql +-- 复合排序:紧急订单优先,然后按金额降序,最后按时间升序 +SELECT customer_name, product_name, amount, status, priority, order_date, + CASE + WHEN status = 'pending' AND priority = 1 THEN '紧急待处理' + WHEN status = 'processing' AND priority <= 2 THEN '优先处理中' + WHEN status = 'completed' THEN '已完成' + ELSE '普通订单' + END AS order_category +FROM orders +ORDER BY + CASE WHEN status = 'pending' AND priority = 1 THEN 1 + WHEN status = 'processing' AND priority <= 2 THEN 2 + WHEN status = 'pending' THEN 3 + WHEN status = 'processing' THEN 4 + WHEN status = 'completed' THEN 5 + ELSE 6 END, + amount DESC, + order_date ASC; +``` + +### 实际案例2:学生成绩分析 + +**场景**:学校需要按多个条件对学生排序。 + +```sql +-- 综合排序:优秀学生优先(分数>90),然后按班级、分数排序 +SELECT name, class_id, score, age, + CASE + WHEN score >= 90 THEN '优秀' + WHEN score >= 80 THEN '良好' + WHEN score >= 70 THEN '中等' + ELSE '待提高' + END AS grade_level +FROM students +ORDER BY + CASE WHEN score >= 90 THEN 1 ELSE 2 END, -- 优秀学生优先 + class_id, -- 再按班级 + score DESC, -- 最后按分数降序 + age; -- 分数相同时按年龄升序 +``` + +### 实际案例3:员工绩效排序 + +**场景**:HR部门需要按绩效和多个因素对员工排序。 + +```sql +-- 复杂绩效排序 +SELECT name, department, salary, performance_score, bonus, hire_date, + CASE + WHEN performance_score >= 9.0 THEN 'A' + WHEN performance_score >= 8.0 THEN 'B' + WHEN performance_score >= 7.0 THEN 'C' + ELSE 'D' + END AS performance_grade +FROM staff +WHERE performance_score IS NOT NULL +ORDER BY + performance_score DESC, -- 绩效评分降序 + CASE WHEN department = '技术部' THEN 1 -- 技术部优先 + WHEN department = '产品部' THEN 2 + ELSE 3 END, + salary DESC, -- 薪资降序 + hire_date; -- 入职时间升序(资历) +``` + +### 面试题4:连续排名问题 + +**题目**:为员工按薪资排序,要求排名连续(即使薪资相同)。 + +```sql +-- 使用不同的排名函数 +SELECT name, salary, + ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num, -- 连续排名 + RANK() OVER (ORDER BY salary DESC) AS rank_num, -- 跳跃排名 + DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank -- 密集排名 +FROM staff +ORDER BY salary DESC, name; +``` + +### 面试题5:移动平均排序 + +**题目**:计算每个学生的班级排名和移动平均分。 + +```sql +-- 窗口函数实现移动平均 +SELECT name, class_id, score, + ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS class_rank, + ROUND(AVG(score) OVER ( + PARTITION BY class_id + ORDER BY score DESC + ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING + ), 2) AS moving_avg +FROM students +WHERE score IS NOT NULL +ORDER BY class_id, class_rank; +``` + +## 性能优化和最佳实践 + +### 1. 索引优化 + +```sql +-- 为经常排序的列创建索引 +CREATE INDEX idx_students_score ON students(score); +CREATE INDEX idx_orders_date_amount ON orders(order_date, amount); +CREATE INDEX idx_staff_dept_salary ON staff(department, salary DESC); + +-- 复合索引的排序优化 +CREATE INDEX idx_orders_status_priority_amount ON orders(status, priority, amount DESC); +``` + +### 2. LIMIT 与 ORDER BY 结合使用 + +```sql +-- ✅ 推荐:只获取需要的记录数 +SELECT name, score FROM students ORDER BY score DESC LIMIT 10; + +-- ❌ 避免:不必要的全表排序 +SELECT name, score FROM students ORDER BY score DESC; -- 然后在应用层取前10条 +``` + +### 3. 避免文件排序 + +```sql +-- ✅ 使用索引排序(Using index) +EXPLAIN SELECT name, score FROM students ORDER BY score DESC; + +-- 查看执行计划中是否出现 "Using filesort" +-- 出现则表示需要文件排序,性能较差 +``` + +### 4. 排序字段选择 + +```sql +-- ✅ 推荐:使用数值型字段排序 +SELECT * FROM orders ORDER BY amount DESC; + +-- ⚠️ 注意:字符串排序相对较慢 +SELECT * FROM orders ORDER BY customer_name; + +-- ✅ 优化:为常用字符串排序创建专门索引 +CREATE INDEX idx_orders_customer_name ON orders(customer_name); +``` + +### 5. 内存使用优化 + +```sql +-- 调整排序缓冲区大小(根据实际情况) +SET SESSION sort_buffer_size = 2097152; -- 2MB + +-- 监控排序性能 +SHOW STATUS LIKE 'Sort%'; +``` + +### 6. 常见性能陷阱 + +```sql +-- ❌ 避免:在ORDER BY中使用函数 +SELECT * FROM students ORDER BY UPPER(name); + +-- ✅ 推荐:创建函数索引或使用计算列 +ALTER TABLE students ADD COLUMN name_upper VARCHAR(50) GENERATED ALWAYS AS (UPPER(name)); +CREATE INDEX idx_students_name_upper ON students(name_upper); + +-- ❌ 避免:复杂的CASE WHEN排序 +SELECT * FROM orders +ORDER BY CASE WHEN status = 'pending' THEN 1 + WHEN status = 'processing' THEN 2 + ELSE 3 END, amount DESC; + +-- ✅ 推荐:添加排序辅助列 +ALTER TABLE orders ADD COLUMN status_sort_order INT; +UPDATE orders SET status_sort_order = CASE + WHEN status = 'pending' THEN 1 + WHEN status = 'processing' THEN 2 + ELSE 3 END; +CREATE INDEX idx_orders_status_sort ON orders(status_sort_order, amount DESC); +``` + +### 7. 监控和调优 + +```sql +-- 查看慢查询日志中的排序相关查询 +SHOW VARIABLES LIKE 'slow_query_log%'; + +-- 分析排序操作的性能 +SELECT + sql_text, + exec_count, + avg_timer_wait/1000000000000 as 'avg_time_sec' +FROM performance_schema.events_statements_summary_by_digest +WHERE sql_text LIKE '%ORDER BY%' +ORDER BY avg_timer_wait DESC +LIMIT 10; +``` + +--- + +**总结:** +- ORDER BY 是数据排序的核心工具 +- 合理使用索引可以显著提升排序性能 +- 多列排序时注意优先级和索引设计 +- 避免在排序字段上使用函数计算 +- 结合 LIMIT 使用可以优化大数据量查询 +- 理解不同排名函数的差异和适用场景 \ No newline at end of file diff --git a/MySQL_RANGE查询优化详解.md b/MySQL_RANGE查询优化详解.md new file mode 100644 index 0000000..72df3e8 --- /dev/null +++ b/MySQL_RANGE查询优化详解.md @@ -0,0 +1,956 @@ +# MySQL RANGE 查询优化详解 + +## 目录 +1. [RANGE 查询基本概念](#range-查询基本概念) +2. [RANGE 查询原理和类型](#range-查询原理和类型) +3. [示例数据准备](#示例数据准备) +4. [RANGE 查询优化技巧](#range-查询优化技巧) +5. [索引策略和执行计划分析](#索引策略和执行计划分析) +6. [实际开发场景案例](#实际开发场景案例) +7. [分区表与 RANGE 优化](#分区表与-range-优化) +8. [性能监控和调优](#性能监控和调优) +9. [常见问题和最佳实践](#常见问题和最佳实践) + +## RANGE 查询基本概念 + +### 什么是 RANGE 查询 + +RANGE 查询是指查询某个字段在指定范围内的数据,在 MySQL 执行计划中表现为 `type=range`。这类查询在实际开发中非常常见,包括: + +- 时间范围查询:`WHERE create_time BETWEEN '2024-01-01' AND '2024-12-31'` +- 数值范围查询:`WHERE price BETWEEN 100 AND 500` +- 大于小于查询:`WHERE id > 1000 AND id < 2000` +- IN 查询:`WHERE status IN ('active', 'pending')` + +### RANGE 查询的重要性 + +```sql +-- 典型的业务场景 +-- 1. 订单查询:查询某个时间段的订单 +SELECT * FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31'; + +-- 2. 商品筛选:查询某个价格区间的商品 +SELECT * FROM products WHERE price >= 100 AND price <= 500; + +-- 3. 日志分析:查询某个ID范围的日志 +SELECT * FROM logs WHERE id > 10000 AND id <= 20000; + +-- 4. 状态筛选:查询多个状态的记录 +SELECT * FROM users WHERE status IN ('active', 'premium', 'vip'); +``` + +## RANGE 查询原理和类型 + +### 1. MySQL 中 RANGE 查询的类型 + +| 类型 | 描述 | 示例 | +|------|------|------| +| **range** | 索引范围扫描 | `WHERE id BETWEEN 1 AND 100` | +| **index_merge** | 多个索引合并 | `WHERE id > 100 OR name = 'test'` | +| **ref_or_null** | 引用查询包含NULL | `WHERE id = 1 OR id IS NULL` | + +### 2. RANGE 查询的执行流程 + +```sql +-- 执行流程示意 +1. 解析 SQL 语句和条件 +2. 选择最优索引 +3. 定位起始位置(起始键值) +4. 扫描到结束位置(结束键值) +5. 返回符合条件的记录 +``` + +### 3. 索引选择性和基数 + +```sql +-- 查看索引选择性 +SELECT + COUNT(DISTINCT column_name) / COUNT(*) as selectivity, + COUNT(DISTINCT column_name) as cardinality, + COUNT(*) as total_rows +FROM table_name; + +-- 选择性越高,索引效果越好 +-- 选择性 = 不重复值数量 / 总行数 +-- 选择性接近 1 表示索引效果最好 +``` + +## 示例数据准备 + +### 创建测试表和数据 + +```sql +-- 创建订单表 +CREATE TABLE orders ( + order_id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id INT NOT NULL, + product_id INT NOT NULL, + order_date DATE NOT NULL, + order_time DATETIME NOT NULL, + amount DECIMAL(10,2) NOT NULL, + quantity INT NOT NULL, + status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') NOT NULL, + region VARCHAR(50) NOT NULL, + channel VARCHAR(30) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + KEY idx_user_id (user_id), + KEY idx_product_id (product_id), + KEY idx_order_date (order_date), + KEY idx_order_time (order_time), + KEY idx_amount (amount), + KEY idx_status (status), + KEY idx_region (region), + KEY idx_user_date (user_id, order_date), + KEY idx_status_date (status, order_date), + KEY idx_amount_date (amount, order_date) +) ENGINE=InnoDB; + +-- 创建商品表 +CREATE TABLE products ( + product_id INT PRIMARY KEY AUTO_INCREMENT, + product_name VARCHAR(200) NOT NULL, + category_id INT NOT NULL, + price DECIMAL(8,2) NOT NULL, + cost DECIMAL(8,2) NOT NULL, + stock_quantity INT NOT NULL, + weight DECIMAL(6,3), + created_date DATE NOT NULL, + last_update_time DATETIME NOT NULL, + status TINYINT NOT NULL DEFAULT 1, + + KEY idx_category (category_id), + KEY idx_price (price), + KEY idx_stock (stock_quantity), + KEY idx_created_date (created_date), + KEY idx_status (status), + KEY idx_category_price (category_id, price), + KEY idx_status_price (status, price) +) ENGINE=InnoDB; + +-- 创建用户访问日志表 +CREATE TABLE access_logs ( + log_id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id INT, + ip_address VARCHAR(45), + request_url VARCHAR(500), + request_method VARCHAR(10), + response_code INT, + response_time_ms INT, + user_agent TEXT, + access_time DATETIME NOT NULL, + session_id VARCHAR(64), + + KEY idx_user_id (user_id), + KEY idx_access_time (access_time), + KEY idx_response_code (response_code), + KEY idx_response_time (response_time_ms), + KEY idx_user_time (user_id, access_time), + KEY idx_code_time (response_code, access_time) +) ENGINE=InnoDB; + +-- 插入测试数据(使用存储过程快速生成大量数据) +DELIMITER // +CREATE PROCEDURE GenerateTestData() +BEGIN + DECLARE i INT DEFAULT 1; + DECLARE max_records INT DEFAULT 100000; + + -- 清空表 + TRUNCATE TABLE orders; + TRUNCATE TABLE products; + TRUNCATE TABLE access_logs; + + -- 生成商品数据 + WHILE i <= 1000 DO + INSERT INTO products ( + product_name, category_id, price, cost, stock_quantity, + weight, created_date, last_update_time, status + ) VALUES ( + CONCAT('产品_', i), + (i % 50) + 1, + ROUND(RAND() * 1000 + 10, 2), + ROUND(RAND() * 500 + 5, 2), + FLOOR(RAND() * 1000), + ROUND(RAND() * 10, 3), + DATE_SUB(CURDATE(), INTERVAL FLOOR(RAND() * 365) DAY), + DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30) DAY), + IF(RAND() > 0.1, 1, 0) + ); + SET i = i + 1; + END WHILE; + + -- 生成订单数据 + SET i = 1; + WHILE i <= max_records DO + INSERT INTO orders ( + user_id, product_id, order_date, order_time, amount, quantity, + status, region, channel + ) VALUES ( + FLOOR(RAND() * 10000) + 1, + FLOOR(RAND() * 1000) + 1, + DATE_SUB(CURDATE(), INTERVAL FLOOR(RAND() * 365) DAY), + DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365 * 24 * 60) MINUTE), + ROUND(RAND() * 1000 + 10, 2), + FLOOR(RAND() * 5) + 1, + ELT(FLOOR(RAND() * 5) + 1, 'pending', 'paid', 'shipped', 'delivered', 'cancelled'), + ELT(FLOOR(RAND() * 6) + 1, '北京', '上海', '广州', '深圳', '杭州', '成都'), + ELT(FLOOR(RAND() * 4) + 1, 'web', 'mobile', 'api', 'admin') + ); + + IF i % 10000 = 0 THEN + COMMIT; + END IF; + SET i = i + 1; + END WHILE; + + -- 生成访问日志数据 + SET i = 1; + WHILE i <= max_records DO + INSERT INTO access_logs ( + user_id, ip_address, request_url, request_method, + response_code, response_time_ms, access_time, session_id + ) VALUES ( + IF(RAND() > 0.3, FLOOR(RAND() * 10000) + 1, NULL), + CONCAT( + FLOOR(RAND() * 255), '.', + FLOOR(RAND() * 255), '.', + FLOOR(RAND() * 255), '.', + FLOOR(RAND() * 255) + ), + CONCAT('/api/v1/', ELT(FLOOR(RAND() * 5) + 1, 'users', 'orders', 'products', 'search', 'cart')), + ELT(FLOOR(RAND() * 4) + 1, 'GET', 'POST', 'PUT', 'DELETE'), + ELT(FLOOR(RAND() * 10) + 1, 200, 200, 200, 200, 200, 404, 500, 403, 401, 302), + FLOOR(RAND() * 5000) + 10, + DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30 * 24 * 60) MINUTE), + MD5(CONCAT(i, RAND())) + ); + + IF i % 10000 = 0 THEN + COMMIT; + END IF; + SET i = i + 1; + END WHILE; + + COMMIT; +END // +DELIMITER ; + +-- 执行数据生成(注意:这会花费一些时间) +CALL GenerateTestData(); + +-- 更新表统计信息 +ANALYZE TABLE orders, products, access_logs; +``` + +## RANGE 查询优化技巧 + +### 1. 日期范围查询优化 + +```sql +-- ❌ 低效的日期查询(无法使用索引) +SELECT * FROM orders +WHERE YEAR(order_date) = 2024 AND MONTH(order_date) = 1; + +-- ❌ 函数导致全表扫描 +SELECT * FROM orders +WHERE DATE_FORMAT(order_date, '%Y-%m') = '2024-01'; + +-- ✅ 高效的日期范围查询 +SELECT * FROM orders +WHERE order_date >= '2024-01-01' AND order_date < '2024-02-01'; + +-- ✅ 使用 BETWEEN(包含边界) +SELECT * FROM orders +WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31'; + +-- 执行计划对比 +EXPLAIN FORMAT=JSON +SELECT * FROM orders +WHERE order_date >= '2024-01-01' AND order_date < '2024-02-01'; +``` + +### 2. 数值范围查询优化 + +```sql +-- ✅ 基本数值范围查询 +SELECT * FROM products +WHERE price BETWEEN 100 AND 500; + +-- ✅ 组合条件优化 +SELECT * FROM products +WHERE price >= 100 AND price <= 500 + AND status = 1; + +-- 🔍 查看执行计划 +EXPLAIN FORMAT=JSON +SELECT * FROM products +WHERE price >= 100 AND price <= 500 AND status = 1; + +-- ✅ 多条件范围查询优化 +SELECT * FROM orders +WHERE amount >= 100 AND amount <= 1000 + AND order_date >= '2024-01-01' AND order_date <= '2024-12-31'; + +-- 🔍 分析索引选择 +EXPLAIN FORMAT=JSON +SELECT * FROM orders +WHERE amount >= 100 AND amount <= 1000 + AND order_date >= '2024-01-01' AND order_date <= '2024-12-31'; +``` + +### 3. IN 查询优化 + +```sql +-- ✅ 基本 IN 查询 +SELECT * FROM orders WHERE status IN ('paid', 'shipped', 'delivered'); + +-- ✅ 大量 IN 值的优化 +SELECT * FROM orders +WHERE user_id IN (1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50); + +-- ❌ 避免过多的 IN 值(可能导致性能问题) +-- SELECT * FROM orders WHERE user_id IN (1,2,3,...,1000); -- 太多值 + +-- ✅ 使用 EXISTS 替代大量 IN 值 +CREATE TEMPORARY TABLE temp_users (user_id INT PRIMARY KEY); +INSERT INTO temp_users VALUES (1), (5), (10), (15), (20); + +SELECT o.* FROM orders o +WHERE EXISTS (SELECT 1 FROM temp_users t WHERE t.user_id = o.user_id); + +-- ✅ 使用 JOIN 替代大量 IN 值 +SELECT o.* FROM orders o +INNER JOIN temp_users t ON o.user_id = t.user_id; +``` + +### 4. 复合索引的 RANGE 查询优化 + +```sql +-- 创建复合索引 +CREATE INDEX idx_user_status_date ON orders(user_id, status, order_date); + +-- ✅ 充分利用复合索引(遵循最左前缀原则) +SELECT * FROM orders +WHERE user_id = 1001 + AND status IN ('paid', 'shipped') + AND order_date >= '2024-01-01'; + +-- ✅ 部分利用复合索引 +SELECT * FROM orders +WHERE user_id = 1001 + AND order_date >= '2024-01-01'; + +-- ❌ 无法利用复合索引(跳过了前缀列) +SELECT * FROM orders +WHERE status = 'paid' + AND order_date >= '2024-01-01'; + +-- 🔍 验证索引使用情况 +EXPLAIN FORMAT=JSON +SELECT * FROM orders +WHERE user_id = 1001 + AND status IN ('paid', 'shipped') + AND order_date >= '2024-01-01'; +``` + +## 索引策略和执行计划分析 + +### 1. 执行计划解读 + +```sql +-- 创建测试查询并分析执行计划 +EXPLAIN FORMAT=JSON +SELECT * FROM orders +WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' + AND amount >= 100; + +-- 关键信息解读: +-- "access_type": "range" - 表示使用了范围扫描 +-- "key": "idx_order_date" - 使用的索引名称 +-- "rows_examined_per_scan": 预估扫描行数 +-- "cost_info": 查询成本信息 +``` + +### 2. 索引选择性分析 + +```sql +-- 分析不同字段的选择性 +SELECT + 'order_date' as column_name, + COUNT(DISTINCT order_date) as distinct_values, + COUNT(*) as total_rows, + COUNT(DISTINCT order_date) / COUNT(*) as selectivity +FROM orders + +UNION ALL + +SELECT + 'amount' as column_name, + COUNT(DISTINCT amount) as distinct_values, + COUNT(*) as total_rows, + COUNT(DISTINCT amount) / COUNT(*) as selectivity +FROM orders + +UNION ALL + +SELECT + 'status' as column_name, + COUNT(DISTINCT status) as distinct_values, + COUNT(*) as total_rows, + COUNT(DISTINCT status) / COUNT(*) as selectivity +FROM orders; + +-- 根据选择性结果调整索引策略 +-- 高选择性字段适合做索引前缀 +-- 低选择性字段适合放在组合索引后面 +``` + +### 3. 成本分析和索引选择 + +```sql +-- 强制使用不同索引对比性能 +-- 使用日期索引 +SELECT * FROM orders USE INDEX (idx_order_date) +WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' + AND amount >= 100; + +-- 使用金额索引 +SELECT * FROM orders USE INDEX (idx_amount) +WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' + AND amount >= 100; + +-- 使用复合索引 +SELECT * FROM orders USE INDEX (idx_amount_date) +WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' + AND amount >= 100; + +-- 让优化器自动选择 +SELECT * FROM orders +WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' + AND amount >= 100; +``` + +### 4. 索引提示的使用 + +```sql +-- 强制使用特定索引 +SELECT * FROM orders FORCE INDEX (idx_order_date) +WHERE order_date >= '2024-01-01' AND amount > 100; + +-- 忽略某个索引 +SELECT * FROM orders IGNORE INDEX (idx_amount) +WHERE order_date >= '2024-01-01' AND amount > 100; + +-- 建议使用某个索引 +SELECT * FROM orders USE INDEX (idx_amount_date) +WHERE order_date >= '2024-01-01' AND amount > 100; +``` + +## 实际开发场景案例 + +### 案例1:电商订单查询系统 + +```sql +-- 需求:查询某用户在指定时间段内的订单,按金额排序 +-- 原始查询(可能性能较差) +SELECT * FROM orders +WHERE user_id = 1001 + AND order_date BETWEEN '2024-01-01' AND '2024-01-31' +ORDER BY amount DESC; + +-- 优化方案1:创建专门的复合索引 +CREATE INDEX idx_user_date_amount ON orders(user_id, order_date, amount DESC); + +-- 优化后的查询 +SELECT * FROM orders +WHERE user_id = 1001 + AND order_date BETWEEN '2024-01-01' AND '2024-01-31' +ORDER BY amount DESC; + +-- 验证优化效果 +EXPLAIN FORMAT=JSON +SELECT * FROM orders +WHERE user_id = 1001 + AND order_date BETWEEN '2024-01-01' AND '2024-01-31' +ORDER BY amount DESC; + +-- 如果需要分页 +SELECT * FROM orders +WHERE user_id = 1001 + AND order_date BETWEEN '2024-01-01' AND '2024-01-31' +ORDER BY amount DESC +LIMIT 20 OFFSET 0; +``` + +### 案例2:实时数据分析查询 + +```sql +-- 需求:按小时统计最近24小时的订单数量和金额 +-- 原始查询 +SELECT + DATE_FORMAT(order_time, '%Y-%m-%d %H:00:00') as hour_bucket, + COUNT(*) as order_count, + SUM(amount) as total_amount +FROM orders +WHERE order_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR) +GROUP BY DATE_FORMAT(order_time, '%Y-%m-%d %H:00:00') +ORDER BY hour_bucket; + +-- 优化方案:创建时间索引并使用覆盖索引 +CREATE INDEX idx_order_time_amount ON orders(order_time, amount); + +-- 优化查询(避免函数计算) +SELECT + FROM_UNIXTIME(UNIX_TIMESTAMP(order_time) - UNIX_TIMESTAMP(order_time) % 3600) as hour_bucket, + COUNT(*) as order_count, + SUM(amount) as total_amount +FROM orders +WHERE order_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR) +GROUP BY FROM_UNIXTIME(UNIX_TIMESTAMP(order_time) - UNIX_TIMESTAMP(order_time) % 3600) +ORDER BY hour_bucket; + +-- 进一步优化:预计算小时桶 +ALTER TABLE orders ADD COLUMN hour_bucket DATETIME GENERATED ALWAYS AS + (FROM_UNIXTIME(UNIX_TIMESTAMP(order_time) - UNIX_TIMESTAMP(order_time) % 3600)) STORED; + +CREATE INDEX idx_hour_bucket_amount ON orders(hour_bucket, amount); + +-- 最优化查询 +SELECT + hour_bucket, + COUNT(*) as order_count, + SUM(amount) as total_amount +FROM orders +WHERE hour_bucket >= DATE_SUB(DATE_SUB(NOW(), INTERVAL MINUTE(NOW()) MINUTE), INTERVAL 23 HOUR) +GROUP BY hour_bucket +ORDER BY hour_bucket; +``` + +### 案例3:用户行为分析 + +```sql +-- 需求:分析响应时间在不同区间的请求分布 +-- 创建响应时间分析的优化索引 +CREATE INDEX idx_response_time_code ON access_logs(response_time_ms, response_code); + +-- 分析查询 +SELECT + CASE + WHEN response_time_ms < 100 THEN '< 100ms' + WHEN response_time_ms < 500 THEN '100-500ms' + WHEN response_time_ms < 1000 THEN '500ms-1s' + WHEN response_time_ms < 3000 THEN '1-3s' + ELSE '> 3s' + END as response_time_bucket, + COUNT(*) as request_count, + COUNT(CASE WHEN response_code = 200 THEN 1 END) as success_count, + ROUND(AVG(response_time_ms), 2) as avg_response_time +FROM access_logs +WHERE access_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) +GROUP BY + CASE + WHEN response_time_ms < 100 THEN '< 100ms' + WHEN response_time_ms < 500 THEN '100-500ms' + WHEN response_time_ms < 1000 THEN '500ms-1s' + WHEN response_time_ms < 3000 THEN '1-3s' + ELSE '> 3s' + END +ORDER BY + CASE + WHEN response_time_ms < 100 THEN 1 + WHEN response_time_ms < 500 THEN 2 + WHEN response_time_ms < 1000 THEN 3 + WHEN response_time_ms < 3000 THEN 4 + ELSE 5 + END; + +-- 优化版本:使用多个简单的RANGE查询代替复杂CASE +SELECT '< 100ms' as bucket, COUNT(*) as count FROM access_logs +WHERE access_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND response_time_ms < 100 +UNION ALL +SELECT '100-500ms', COUNT(*) FROM access_logs +WHERE access_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND response_time_ms >= 100 AND response_time_ms < 500 +UNION ALL +SELECT '500ms-1s', COUNT(*) FROM access_logs +WHERE access_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND response_time_ms >= 500 AND response_time_ms < 1000 +UNION ALL +SELECT '1-3s', COUNT(*) FROM access_logs +WHERE access_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND response_time_ms >= 1000 AND response_time_ms < 3000 +UNION ALL +SELECT '> 3s', COUNT(*) FROM access_logs +WHERE access_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND response_time_ms >= 3000; +``` + +## 分区表与 RANGE 优化 + +### 1. 基于日期的范围分区 + +```sql +-- 创建按月份分区的订单表 +CREATE TABLE orders_partitioned ( + order_id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id INT NOT NULL, + product_id INT NOT NULL, + order_date DATE NOT NULL, + order_time DATETIME NOT NULL, + amount DECIMAL(10,2) NOT NULL, + status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') NOT NULL, + region VARCHAR(50) NOT NULL, + + KEY idx_user_id (user_id), + KEY idx_order_date (order_date), + KEY idx_amount (amount), + KEY idx_user_date (user_id, order_date) +) ENGINE=InnoDB +PARTITION BY RANGE (YEAR(order_date) * 100 + MONTH(order_date)) ( + PARTITION p202401 VALUES LESS THAN (202402), + PARTITION p202402 VALUES LESS THAN (202403), + PARTITION p202403 VALUES LESS THAN (202404), + PARTITION p202404 VALUES LESS THAN (202405), + PARTITION p202405 VALUES LESS THAN (202406), + PARTITION p202406 VALUES LESS THAN (202407), + PARTITION p202407 VALUES LESS THAN (202408), + PARTITION p202408 VALUES LESS THAN (202409), + PARTITION p202409 VALUES LESS THAN (202410), + PARTITION p202410 VALUES LESS THAN (202411), + PARTITION p202411 VALUES LESS THAN (202412), + PARTITION p202412 VALUES LESS THAN (202501), + PARTITION p_future VALUES LESS THAN MAXVALUE +); + +-- 分区表的RANGE查询优化 +-- ✅ 可以利用分区剪枝的查询 +SELECT * FROM orders_partitioned +WHERE order_date BETWEEN '2024-03-01' AND '2024-03-31'; + +-- 查看分区剪枝效果 +EXPLAIN PARTITIONS +SELECT * FROM orders_partitioned +WHERE order_date BETWEEN '2024-03-01' AND '2024-03-31'; + +-- ❌ 无法利用分区剪枝的查询(使用函数) +SELECT * FROM orders_partitioned +WHERE YEAR(order_date) = 2024 AND MONTH(order_date) = 3; +``` + +### 2. 基于数值的范围分区 + +```sql +-- 创建按用户ID范围分区的表 +CREATE TABLE user_activities_partitioned ( + activity_id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id INT NOT NULL, + activity_type VARCHAR(50) NOT NULL, + activity_time DATETIME NOT NULL, + activity_data JSON, + + KEY idx_user_time (user_id, activity_time), + KEY idx_activity_time (activity_time) +) ENGINE=InnoDB +PARTITION BY RANGE (user_id) ( + PARTITION p0 VALUES LESS THAN (1000), + PARTITION p1 VALUES LESS THAN (5000), + PARTITION p2 VALUES LESS THAN (10000), + PARTITION p3 VALUES LESS THAN (50000), + PARTITION p4 VALUES LESS THAN MAXVALUE +); + +-- 针对特定用户范围的查询优化 +SELECT * FROM user_activities_partitioned +WHERE user_id BETWEEN 1000 AND 4999 + AND activity_time >= '2024-01-01'; + +-- 查看执行计划 +EXPLAIN PARTITIONS +SELECT * FROM user_activities_partitioned +WHERE user_id BETWEEN 1000 AND 4999 + AND activity_time >= '2024-01-01'; +``` + +### 3. 分区表维护 + +```sql +-- 添加新分区 +ALTER TABLE orders_partitioned +ADD PARTITION (PARTITION p202501 VALUES LESS THAN (202502)); + +-- 删除旧分区(删除数据) +ALTER TABLE orders_partitioned DROP PARTITION p202401; + +-- 重组分区 +ALTER TABLE orders_partitioned +REORGANIZE PARTITION p_future INTO ( + PARTITION p202502 VALUES LESS THAN (202503), + PARTITION p202503 VALUES LESS THAN (202504), + PARTITION p_future VALUES LESS THAN MAXVALUE +); + +-- 查看分区信息 +SELECT + PARTITION_NAME, + TABLE_ROWS, + DATA_LENGTH, + PARTITION_DESCRIPTION +FROM information_schema.PARTITIONS +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'orders_partitioned'; +``` + +## 性能监控和调优 + +### 1. 监控 RANGE 查询性能 + +```sql +-- 查看慢查询中的RANGE查询 +SELECT + sql_text, + exec_count, + avg_timer_wait/1000000000000 as avg_time_sec, + sum_rows_examined/exec_count as avg_rows_examined, + sum_rows_sent/exec_count as avg_rows_sent, + (sum_rows_examined/exec_count) / (sum_rows_sent/exec_count) as examine_ratio +FROM performance_schema.events_statements_summary_by_digest +WHERE sql_text LIKE '%BETWEEN%' + OR sql_text LIKE '%>%' + OR sql_text LIKE '%<%' +ORDER BY avg_timer_wait DESC +LIMIT 10; + +-- 监控索引使用情况 +SELECT + object_schema, + object_name, + index_name, + count_star as usage_count, + sum_timer_wait/1000000000000 as total_time_sec +FROM performance_schema.table_io_waits_summary_by_index_usage +WHERE object_schema = DATABASE() + AND count_star > 0 +ORDER BY sum_timer_wait DESC; +``` + +### 2. 性能测试脚本 + +```sql +-- 创建性能测试存储过程 +DELIMITER // +CREATE PROCEDURE TestRangeQueryPerformance() +BEGIN + DECLARE start_time DATETIME; + DECLARE end_time DATETIME; + DECLARE duration_ms INT; + + -- 测试1:日期范围查询 + SET start_time = NOW(6); + SELECT COUNT(*) FROM orders + WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31'; + SET end_time = NOW(6); + SET duration_ms = TIMESTAMPDIFF(MICROSECOND, start_time, end_time) / 1000; + SELECT 'Date Range Query' as test_name, duration_ms as duration_ms; + + -- 测试2:数值范围查询 + SET start_time = NOW(6); + SELECT COUNT(*) FROM orders + WHERE amount BETWEEN 100 AND 500; + SET end_time = NOW(6); + SET duration_ms = TIMESTAMPDIFF(MICROSECOND, start_time, end_time) / 1000; + SELECT 'Amount Range Query' as test_name, duration_ms as duration_ms; + + -- 测试3:复合条件查询 + SET start_time = NOW(6); + SELECT COUNT(*) FROM orders + WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' + AND amount BETWEEN 100 AND 500; + SET end_time = NOW(6); + SET duration_ms = TIMESTAMPDIFF(MICROSECOND, start_time, end_time) / 1000; + SELECT 'Combined Range Query' as test_name, duration_ms as duration_ms; + +END // +DELIMITER ; + +-- 执行性能测试 +CALL TestRangeQueryPerformance(); +``` + +### 3. 自动化索引建议 + +```sql +-- 分析表的查询模式,建议索引 +CREATE VIEW range_query_analysis AS +SELECT + 'orders' as table_name, + 'order_date' as column_name, + COUNT(DISTINCT order_date) as distinct_values, + COUNT(*) as total_rows, + COUNT(DISTINCT order_date) / COUNT(*) as selectivity, + CASE + WHEN COUNT(DISTINCT order_date) / COUNT(*) > 0.1 THEN '高选择性,建议单独建索引' + WHEN COUNT(DISTINCT order_date) / COUNT(*) > 0.01 THEN '中等选择性,建议组合索引' + ELSE '低选择性,不建议建索引' + END as index_recommendation +FROM orders + +UNION ALL + +SELECT + 'orders' as table_name, + 'amount' as column_name, + COUNT(DISTINCT amount) as distinct_values, + COUNT(*) as total_rows, + COUNT(DISTINCT amount) / COUNT(*) as selectivity, + CASE + WHEN COUNT(DISTINCT amount) / COUNT(*) > 0.1 THEN '高选择性,建议单独建索引' + WHEN COUNT(DISTINCT amount) / COUNT(*) > 0.01 THEN '中等选择性,建议组合索引' + ELSE '低选择性,不建议建索引' + END as index_recommendation +FROM orders; + +-- 查看分析结果 +SELECT * FROM range_query_analysis; +``` + +## 常见问题和最佳实践 + +### 1. 常见性能陷阱 + +```sql +-- ❌ 陷阱1:在WHERE条件中使用函数 +-- 错误示例 +SELECT * FROM orders WHERE YEAR(order_date) = 2024; +SELECT * FROM orders WHERE DATE_ADD(order_date, INTERVAL 1 DAY) = '2024-01-02'; + +-- ✅ 正确做法 +SELECT * FROM orders WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'; +SELECT * FROM orders WHERE order_date = '2024-01-01'; + +-- ❌ 陷阱2:数据类型不匹配导致的隐式转换 +-- 错误示例(假设user_id是INT类型) +SELECT * FROM orders WHERE user_id = '1001'; -- 字符串比较整数 + +-- ✅ 正确做法 +SELECT * FROM orders WHERE user_id = 1001; + +-- ❌ 陷阱3:LIKE查询的误用 +-- 错误示例 +SELECT * FROM orders WHERE order_id LIKE '1001%'; -- 应该用数值比较 + +-- ✅ 正确做法 +SELECT * FROM orders WHERE order_id >= 1001 AND order_id < 1002; +``` + +### 2. 索引设计最佳实践 + +```sql +-- ✅ 最佳实践1:根据查询频率设计索引 +-- 高频查询的索引设计 +CREATE INDEX idx_user_status_date ON orders(user_id, status, order_date); +CREATE INDEX idx_date_amount ON orders(order_date, amount); + +-- ✅ 最佳实践2:考虑排序需求的索引设计 +-- 如果经常需要按金额降序排序 +CREATE INDEX idx_date_amount_desc ON orders(order_date, amount DESC); + +-- ✅ 最佳实践3:覆盖索引的设计 +-- 如果只查询特定列,可以设计覆盖索引 +CREATE INDEX idx_covering_order_summary ON orders(order_date, user_id, amount, status); + +-- 该查询只需访问索引,无需回表 +SELECT user_id, amount, status FROM orders +WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31'; +``` + +### 3. 查询优化检查清单 + +```sql +-- 优化检查清单 +/* +1. ✅ 避免在WHERE条件中使用函数 +2. ✅ 确保数据类型匹配,避免隐式转换 +3. ✅ 合理使用复合索引,遵循最左前缀原则 +4. ✅ 考虑使用覆盖索引减少回表操作 +5. ✅ 对于大量IN值,考虑使用JOIN或EXISTS +6. ✅ 利用分区表的分区剪枝特性 +7. ✅ 定期分析表统计信息 +8. ✅ 监控慢查询日志和执行计划 +9. ✅ 考虑查询结果的数据量,适当使用LIMIT +10. ✅ 根据业务特点选择合适的索引策略 +*/ + +-- 索引效果验证模板 +EXPLAIN FORMAT=JSON SELECT * FROM table_name WHERE conditions; + +-- 性能对比模板 +SELECT + BENCHMARK(10000, (SELECT COUNT(*) FROM table_name WHERE conditions)) as execution_time; +``` + +### 4. 实际生产环境建议 + +```sql +-- 生产环境优化建议 + +-- 1. 定期收集表统计信息 +ANALYZE TABLE orders, products, access_logs; + +-- 2. 监控索引使用情况 +CREATE EVENT check_index_usage +ON SCHEDULE EVERY 1 DAY +DO + INSERT INTO index_usage_log + SELECT + NOW() as check_time, + object_name, + index_name, + count_star as usage_count + FROM performance_schema.table_io_waits_summary_by_index_usage + WHERE object_schema = DATABASE() + AND count_star = 0; -- 未使用的索引 + +-- 3. 自动优化建议 +DELIMITER // +CREATE PROCEDURE GenerateOptimizationSuggestions() +BEGIN + -- 查找可能需要索引的列 + SELECT + 'Consider adding index' as suggestion, + CONCAT('CREATE INDEX idx_', table_name, '_', column_name, + ' ON ', table_name, '(', column_name, ');') as sql_statement + FROM ( + SELECT 'orders' as table_name, 'region' as column_name + UNION ALL + SELECT 'products', 'category_id' + UNION ALL + SELECT 'access_logs', 'user_agent' + ) potential_indexes; + + -- 查找可能需要删除的索引 + SELECT + 'Consider dropping unused index' as suggestion, + CONCAT('DROP INDEX ', index_name, ' ON ', object_name, ';') as sql_statement + FROM performance_schema.table_io_waits_summary_by_index_usage + WHERE object_schema = DATABASE() + AND count_star = 0 + AND index_name != 'PRIMARY'; +END // +DELIMITER ; + +-- 调用优化建议 +CALL GenerateOptimizationSuggestions(); +``` + +--- + +**总结:** + +RANGE 查询优化是数据库性能优化的重要组成部分,关键要点包括: + +1. **理解原理**:掌握 RANGE 查询的执行机制和索引选择逻辑 +2. **索引设计**:根据查询模式设计合适的单列和复合索引 +3. **查询优化**:避免函数使用,保证数据类型匹配,合理使用索引提示 +4. **分区策略**:对于大表,考虑使用分区表提高查询效率 +5. **性能监控**:建立完善的监控体系,持续优化查询性能 +6. **最佳实践**:遵循数据库设计和查询优化的最佳实践 + +通过系统的 RANGE 查询优化,可以显著提升数据库查询性能,改善用户体验。 \ No newline at end of file diff --git a/MySQL_UNION语法使用文档.md b/MySQL_UNION语法使用文档.md new file mode 100644 index 0000000..cf26789 --- /dev/null +++ b/MySQL_UNION语法使用文档.md @@ -0,0 +1,532 @@ +# MySQL UNION 语法使用文档 + +## 目录 +1. [UNION 基础语法](#union-基础语法) +2. [UNION 规则和限制](#union-规则和限制) +3. [示例数据准备](#示例数据准备) +4. [UNION 基础使用示例](#union-基础使用示例) +5. [UNION vs UNION ALL](#union-vs-union-all) +6. [复杂查询示例](#复杂查询示例) +7. [最佳实践和注意事项](#最佳实践和注意事项) + +## UNION 基础语法 + +UNION 用于合并两个或多个 SELECT 语句的结果集。 + +```sql +SELECT column1, column2, ... FROM table1 +UNION [ALL] +SELECT column1, column2, ... FROM table2 +[UNION [ALL] +SELECT column1, column2, ... FROM table3] +... +[ORDER BY column_name] +``` + +## UNION 规则和限制 + +1. **列数相同**:每个 SELECT 语句必须拥有相同数量的列 +2. **数据类型兼容**:对应位置的列必须具有兼容的数据类型 +3. **列名**:结果集使用第一个 SELECT 语句的列名 +4. **去重**:UNION 默认去除重复记录,UNION ALL 保留所有记录 +5. **ORDER BY**:只能在最后使用,对整个结果集排序 + +## 示例数据准备 + +### 创建示例表 + +```sql +-- 创建员工表 +CREATE TABLE employees ( + id INT PRIMARY KEY, + name VARCHAR(50), + department VARCHAR(30), + salary DECIMAL(10,2), + city VARCHAR(30) +); + +-- 创建前员工表 +CREATE TABLE former_employees ( + id INT PRIMARY KEY, + name VARCHAR(50), + department VARCHAR(30), + last_salary DECIMAL(10,2), + city VARCHAR(30), + leave_date DATE +); + +-- 创建管理层表 +CREATE TABLE managers ( + id INT PRIMARY KEY, + name VARCHAR(50), + level VARCHAR(20), + salary DECIMAL(10,2), + region VARCHAR(30) +); +``` + +### 插入示例数据 + +```sql +-- 插入员工数据 +INSERT INTO employees (id, name, department, salary, city) VALUES +(1, '张三', '技术部', 8000.00, '北京'), +(2, '李四', '销售部', 6000.00, '上海'), +(3, '王五', '技术部', 9000.00, '深圳'), +(4, '赵六', '人事部', 5500.00, '北京'), +(5, '钱七', '财务部', 7000.00, '广州'), +(6, '孙八', '技术部', 8500.00, '杭州'), +(7, '周九', '市场部', 6500.00, '成都'); + +-- 插入前员工数据 +INSERT INTO former_employees (id, name, department, last_salary, city, leave_date) VALUES +(8, '吴十', '技术部', 7500.00, '北京', '2023-08-15'), +(9, '郑一', '销售部', 5800.00, '上海', '2023-09-20'), +(10, '王五', '技术部', 8000.00, '深圳', '2023-07-10'), +(11, '刘二', '人事部', 5200.00, '广州', '2023-10-05'); + +-- 插入管理层数据 +INSERT INTO managers (id, name, level, salary, region) VALUES +(12, '陈总', '高级经理', 15000.00, '华北'), +(13, '林总', '部门经理', 12000.00, '华东'), +(14, '张三', '项目经理', 10000.00, '华南'), +(15, '黄总', '区域经理', 11000.00, '华西'); +``` + +## UNION 基础使用示例 + +### 1. 简单合并查询 + +合并当前员工和前员工的姓名: + +```sql +SELECT name FROM employees +UNION +SELECT name FROM former_employees; +``` + +**结果:** +``` ++------+ +| name | ++------+ +| 张三 | +| 李四 | +| 王五 | +| 赵六 | +| 钱七 | +| 孙八 | +| 周九 | +| 吴十 | +| 郑一 | +| 刘二 | ++------+ +``` + +### 2. 多列合并查询 + +合并员工和管理层的姓名和薪资: + +```sql +SELECT name, salary FROM employees +UNION +SELECT name, salary FROM managers +ORDER BY salary DESC; +``` + +**结果:** +``` ++------+----------+ +| name | salary | ++------+----------+ +| 陈总 | 15000.00 | +| 林总 | 12000.00 | +| 黄总 | 11000.00 | +| 张三 | 10000.00 | +| 王五 | 9000.00 | +| 孙八 | 8500.00 | +| 张三 | 8000.00 | +| 钱七 | 7000.00 | +| 周九 | 6500.00 | +| 李四 | 6000.00 | +| 赵六 | 5500.00 | ++------+----------+ +``` + +### 3. 使用别名统一列名 + +```sql +SELECT name, department AS work_area, salary FROM employees +UNION +SELECT name, region AS work_area, salary FROM managers +ORDER BY work_area; +``` + +**结果:** +``` ++------+-----------+----------+ +| name | work_area | salary | ++------+-----------+----------+ +| 钱七 | 财务部 | 7000.00 | +| 陈总 | 华北 | 15000.00 | +| 林总 | 华东 | 12000.00 | +| 张三 | 华南 | 10000.00 | +| 黄总 | 华西 | 11000.00 | +| 赵六 | 人事部 | 5500.00 | +| 李四 | 销售部 | 6000.00 | +| 周九 | 市场部 | 6500.00 | +| 张三 | 技术部 | 8000.00 | +| 王五 | 技术部 | 9000.00 | +| 孙八 | 技术部 | 8500.00 | ++------+-----------+----------+ +``` + +## UNION vs UNION ALL + +### UNION(去重) + +```sql +SELECT city FROM employees +UNION +SELECT city FROM former_employees; +``` + +**结果:** +``` ++------+ +| city | ++------+ +| 北京 | +| 上海 | +| 深圳 | +| 广州 | +| 杭州 | +| 成都 | ++------+ +``` + +### UNION ALL(保留重复) + +```sql +SELECT city FROM employees +UNION ALL +SELECT city FROM former_employees; +``` + +**结果:** +``` ++------+ +| city | ++------+ +| 北京 | +| 上海 | +| 深圳 | +| 北京 | +| 广州 | +| 杭州 | +| 成都 | +| 北京 | +| 上海 | +| 深圳 | +| 广州 | ++------+ +``` + +### 性能对比示例 + +```sql +-- 统计重复记录数量 +SELECT + 'UNION' AS query_type, + COUNT(*) AS record_count +FROM ( + SELECT city FROM employees + UNION + SELECT city FROM former_employees +) AS union_result + +UNION ALL + +SELECT + 'UNION ALL' AS query_type, + COUNT(*) AS record_count +FROM ( + SELECT city FROM employees + UNION ALL + SELECT city FROM former_employees +) AS union_all_result; +``` + +**结果:** +``` ++------------+--------------+ +| query_type | record_count | ++------------+--------------+ +| UNION | 6 | +| UNION ALL | 11 | ++------------+--------------+ +``` + +## 复杂查询示例 + +### 1. 条件过滤与UNION + +查询高薪员工和所有管理层: + +```sql +SELECT name, salary, '高薪员工' AS category +FROM employees +WHERE salary > 8000 + +UNION + +SELECT name, salary, '管理层' AS category +FROM managers + +ORDER BY salary DESC; +``` + +**结果:** +``` ++------+----------+----------+ +| name | salary | category | ++------+----------+----------+ +| 陈总 | 15000.00 | 管理层 | +| 林总 | 12000.00 | 管理层 | +| 黄总 | 11000.00 | 管理层 | +| 张三 | 10000.00 | 管理层 | +| 王五 | 9000.00 | 高薪员工 | +| 孙八 | 8500.00 | 高薪员工 | ++------+----------+----------+ +``` + +### 2. 聚合查询与UNION + +各部门薪资统计: + +```sql +SELECT department AS name, AVG(salary) AS avg_salary, '部门平均' AS type +FROM employees +GROUP BY department + +UNION ALL + +SELECT '公司总体' AS name, AVG(salary) AS avg_salary, '总体平均' AS type +FROM employees + +UNION ALL + +SELECT '管理层' AS name, AVG(salary) AS avg_salary, '管理平均' AS type +FROM managers + +ORDER BY avg_salary DESC; +``` + +**结果:** +``` ++----------+-------------+----------+ +| name | avg_salary | type | ++----------+-------------+----------+ +| 管理层 | 12000.00000 | 管理平均 | +| 技术部 | 8500.00000 | 部门平均 | +| 公司总体 | 7142.85714 | 总体平均 | +| 财务部 | 7000.00000 | 部门平均 | +| 市场部 | 6500.00000 | 部门平均 | +| 销售部 | 6000.00000 | 部门平均 | +| 人事部 | 5500.00000 | 部门平均 | ++----------+-------------+----------+ +``` + +### 3. 多表复杂联合查询 + +创建完整的人员名册: + +```sql +SELECT + id, + name, + department, + salary, + city, + '在职' AS status +FROM employees + +UNION ALL + +SELECT + id, + name, + department, + last_salary AS salary, + city, + '离职' AS status +FROM former_employees + +UNION ALL + +SELECT + id, + name, + '管理层' AS department, + salary, + region AS city, + '管理' AS status +FROM managers + +ORDER BY status, salary DESC; +``` + +### 4. 使用子查询的UNION + +查询每个城市的最高薪资员工: + +```sql +SELECT name, city, salary, '当前最高薪' AS note +FROM employees e1 +WHERE salary = ( + SELECT MAX(salary) + FROM employees e2 + WHERE e2.city = e1.city +) + +UNION + +SELECT name, city, last_salary AS salary, '前员工最高薪' AS note +FROM former_employees f1 +WHERE last_salary = ( + SELECT MAX(last_salary) + FROM former_employees f2 + WHERE f2.city = f1.city +) + +ORDER BY salary DESC; +``` + +## 最佳实践和注意事项 + +### 1. 性能优化建议 + +```sql +-- ❌ 避免:不必要的UNION,可以用OR替代 +SELECT * FROM employees WHERE department = '技术部' +UNION +SELECT * FROM employees WHERE department = '销售部'; + +-- ✅ 推荐:使用OR条件 +SELECT * FROM employees +WHERE department IN ('技术部', '销售部'); +``` + +### 2. 数据类型兼容性 + +```sql +-- ✅ 正确:确保数据类型兼容 +SELECT id, name, salary FROM employees +UNION +SELECT id, name, CAST(last_salary AS DECIMAL(10,2)) FROM former_employees; + +-- ❌ 错误:数据类型不兼容可能导致错误 +SELECT id, name, salary FROM employees +UNION +SELECT id, name, leave_date FROM former_employees; -- leave_date是DATE类型 +``` + +### 3. 使用索引优化 + +```sql +-- 为UNION查询中的过滤条件创建索引 +CREATE INDEX idx_employees_dept ON employees(department); +CREATE INDEX idx_employees_salary ON employees(salary); +CREATE INDEX idx_former_employees_dept ON former_employees(department); + +-- 优化后的查询 +SELECT name, department FROM employees WHERE department = '技术部' +UNION +SELECT name, department FROM former_employees WHERE department = '技术部'; +``` + +### 4. 内存使用注意事项 + +```sql +-- 对于大型结果集,考虑使用LIMIT +SELECT name, salary FROM employees +UNION ALL +SELECT name, salary FROM managers +ORDER BY salary DESC +LIMIT 10; +``` + +### 5. 常见错误和解决方案 + +```sql +-- ❌ 错误:列数不匹配 +SELECT name, department FROM employees +UNION +SELECT name FROM managers; -- 缺少一列 + +-- ✅ 修正:补齐列数 +SELECT name, department FROM employees +UNION +SELECT name, level AS department FROM managers; + +-- ❌ 错误:ORDER BY位置错误 +SELECT name FROM employees ORDER BY name +UNION +SELECT name FROM managers ORDER BY name; + +-- ✅ 修正:ORDER BY只能在最后 +SELECT name FROM employees +UNION +SELECT name FROM managers +ORDER BY name; +``` + +### 6. 实用查询模式 + +#### 数据完整性检查 +```sql +-- 检查是否有重复的员工记录 +SELECT name, COUNT(*) as count +FROM ( + SELECT name FROM employees + UNION ALL + SELECT name FROM former_employees + UNION ALL + SELECT name FROM managers +) AS all_people +GROUP BY name +HAVING COUNT(*) > 1; +``` + +#### 数据对比分析 +```sql +-- 对比不同表中的数据分布 +SELECT + '当前员工' AS source, + department, + COUNT(*) AS count, + AVG(salary) AS avg_salary +FROM employees +GROUP BY department + +UNION ALL + +SELECT + '前员工' AS source, + department, + COUNT(*) AS count, + AVG(last_salary) AS avg_salary +FROM former_employees +GROUP BY department + +ORDER BY source, department; +``` + +--- + +**总结:** +- UNION 是合并查询结果的强大工具 +- 注意列数、数据类型的匹配 +- 根据需求选择 UNION 或 UNION ALL +- 合理使用索引和LIMIT提升性能 +- 避免不必要的复杂UNION操作 \ No newline at end of file diff --git a/MySQL常用SQL语法使用文档.md b/MySQL常用SQL语法使用文档.md new file mode 100644 index 0000000..c073485 --- /dev/null +++ b/MySQL常用SQL语法使用文档.md @@ -0,0 +1,1301 @@ +# MySQL 常用SQL语法使用文档 + +## 目录 +1. [SQL基础概念](#sql基础概念) +2. [数据定义语言 DDL](#数据定义语言-ddl) +3. [数据操作语言 DML](#数据操作语言-dml) +4. [数据查询语言 DQL](#数据查询语言-dql) +5. [数据控制语言 DCL](#数据控制语言-dcl) +6. [复杂查询操作](#复杂查询操作) +7. [索引和约束](#索引和约束) +8. [存储过程和函数](#存储过程和函数) +9. [实际项目案例](#实际项目案例) +10. [性能优化和最佳实践](#性能优化和最佳实践) + +## SQL基础概念 + +### SQL语言分类 + +| 类型 | 全称 | 功能 | 主要语句 | +|------|------|------|----------| +| DDL | Data Definition Language | 数据定义 | CREATE, ALTER, DROP | +| DML | Data Manipulation Language | 数据操作 | INSERT, UPDATE, DELETE | +| DQL | Data Query Language | 数据查询 | SELECT | +| DCL | Data Control Language | 数据控制 | GRANT, REVOKE | +| TCL | Transaction Control Language | 事务控制 | COMMIT, ROLLBACK | + +### 基本概念 + +```sql +-- 数据库:存储数据的容器 +-- 表:数据的结构化存储 +-- 行(记录):表中的每一条数据 +-- 列(字段):表中的每一个属性 +-- 主键:唯一标识每行数据的字段 +-- 外键:引用其他表主键的字段 +``` + +## 数据定义语言 DDL + +### 1. 数据库操作 + +```sql +-- 创建数据库 +CREATE DATABASE company_db + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +-- 查看数据库 +SHOW DATABASES; +SHOW CREATE DATABASE company_db; + +-- 修改数据库 +ALTER DATABASE company_db CHARACTER SET utf8mb4; + +-- 删除数据库 +DROP DATABASE IF EXISTS company_db; + +-- 使用数据库 +USE company_db; +``` + +### 2. 数据表操作 + +#### 创建表 + +```sql +-- 员工信息表 +CREATE TABLE employees ( + emp_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID', + emp_code VARCHAR(20) NOT NULL UNIQUE COMMENT '员工编号', + name VARCHAR(50) NOT NULL COMMENT '姓名', + gender ENUM('M', 'F') DEFAULT 'M' COMMENT '性别', + birth_date DATE COMMENT '出生日期', + hire_date DATE NOT NULL DEFAULT (CURRENT_DATE) COMMENT '入职日期', + department_id INT COMMENT '部门ID', + position VARCHAR(50) COMMENT '职位', + salary DECIMAL(10,2) CHECK (salary > 0) COMMENT '薪资', + manager_id INT COMMENT '直属上级ID', + email VARCHAR(100) UNIQUE COMMENT '邮箱', + phone VARCHAR(20) COMMENT '电话', + address TEXT COMMENT '地址', + status ENUM('active', 'inactive', 'terminated') DEFAULT 'active' COMMENT '状态', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + + INDEX idx_department (department_id), + INDEX idx_manager (manager_id), + INDEX idx_hire_date (hire_date), + INDEX idx_name_dept (name, department_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工信息表'; + +-- 部门表 +CREATE TABLE departments ( + dept_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID', + dept_code VARCHAR(20) NOT NULL UNIQUE COMMENT '部门编码', + dept_name VARCHAR(50) NOT NULL COMMENT '部门名称', + parent_id INT COMMENT '上级部门ID', + manager_id INT COMMENT '部门经理ID', + location VARCHAR(100) COMMENT '办公地点', + budget DECIMAL(12,2) COMMENT '部门预算', + description TEXT COMMENT '部门描述', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_parent (parent_id), + INDEX idx_manager (manager_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门表'; + +-- 项目表 +CREATE TABLE projects ( + project_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '项目ID', + project_code VARCHAR(30) NOT NULL UNIQUE COMMENT '项目编码', + project_name VARCHAR(100) NOT NULL COMMENT '项目名称', + description TEXT COMMENT '项目描述', + start_date DATE NOT NULL COMMENT '开始日期', + end_date DATE COMMENT '结束日期', + budget DECIMAL(12,2) COMMENT '项目预算', + actual_cost DECIMAL(12,2) DEFAULT 0 COMMENT '实际成本', + status ENUM('planning', 'active', 'completed', 'cancelled') DEFAULT 'planning' COMMENT '项目状态', + priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium' COMMENT '优先级', + client VARCHAR(100) COMMENT '客户名称', + manager_id INT COMMENT '项目经理ID', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_manager (manager_id), + INDEX idx_status (status), + INDEX idx_dates (start_date, end_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目表'; + +-- 员工项目关联表 +CREATE TABLE employee_projects ( + id INT PRIMARY KEY AUTO_INCREMENT, + emp_id INT NOT NULL COMMENT '员工ID', + project_id INT NOT NULL COMMENT '项目ID', + role VARCHAR(50) COMMENT '在项目中的角色', + allocation_percentage DECIMAL(5,2) DEFAULT 100.00 COMMENT '分配比例', + start_date DATE NOT NULL COMMENT '开始参与日期', + end_date DATE COMMENT '结束参与日期', + hourly_rate DECIMAL(8,2) COMMENT '小时费率', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + UNIQUE KEY uk_emp_project (emp_id, project_id), + INDEX idx_employee (emp_id), + INDEX idx_project (project_id), + INDEX idx_dates (start_date, end_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工项目关联表'; + +-- 考勤表 +CREATE TABLE attendance ( + id INT PRIMARY KEY AUTO_INCREMENT, + emp_id INT NOT NULL COMMENT '员工ID', + work_date DATE NOT NULL COMMENT '工作日期', + check_in_time TIME COMMENT '签到时间', + check_out_time TIME COMMENT '签退时间', + break_hours DECIMAL(4,2) DEFAULT 1.00 COMMENT '休息时间(小时)', + work_hours DECIMAL(4,2) COMMENT '工作时长', + overtime_hours DECIMAL(4,2) DEFAULT 0 COMMENT '加班时长', + status ENUM('present', 'absent', 'late', 'early_leave', 'holiday') DEFAULT 'present' COMMENT '考勤状态', + notes TEXT COMMENT '备注', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + UNIQUE KEY uk_emp_date (emp_id, work_date), + INDEX idx_employee (emp_id), + INDEX idx_date (work_date), + INDEX idx_status (status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='考勤表'; +``` + +#### 修改表结构 + +```sql +-- 添加列 +ALTER TABLE employees +ADD COLUMN emergency_contact VARCHAR(100) COMMENT '紧急联系人' AFTER phone, +ADD COLUMN skills TEXT COMMENT '技能描述'; + +-- 修改列 +ALTER TABLE employees +MODIFY COLUMN salary DECIMAL(12,2) COMMENT '薪资(支持更大金额)', +CHANGE COLUMN emp_code employee_code VARCHAR(25) NOT NULL UNIQUE COMMENT '员工编号'; + +-- 删除列 +ALTER TABLE employees DROP COLUMN emergency_contact; + +-- 添加索引 +ALTER TABLE employees +ADD INDEX idx_email (email), +ADD INDEX idx_status_dept (status, department_id); + +-- 删除索引 +ALTER TABLE employees DROP INDEX idx_email; + +-- 添加外键约束 +ALTER TABLE employees +ADD CONSTRAINT fk_emp_department +FOREIGN KEY (department_id) REFERENCES departments(dept_id), +ADD CONSTRAINT fk_emp_manager +FOREIGN KEY (manager_id) REFERENCES employees(emp_id); + +-- 删除外键约束 +ALTER TABLE employees DROP FOREIGN KEY fk_emp_department; + +-- 重命名表 +RENAME TABLE employees TO staff; +RENAME TABLE staff TO employees; + +-- 查看表结构 +DESC employees; +SHOW CREATE TABLE employees; +SHOW INDEX FROM employees; +``` + +#### 删除表 + +```sql +-- 删除表(如果存在) +DROP TABLE IF EXISTS employee_projects; + +-- 删除多个表 +DROP TABLE IF EXISTS attendance, employee_projects; +``` + +## 数据操作语言 DML + +### 插入示例数据 + +```sql +-- 插入部门数据 +INSERT INTO departments (dept_code, dept_name, parent_id, location, budget, description) VALUES +('TECH', '技术部', NULL, '北京总部A座', 5000000.00, '负责产品研发和技术创新'), +('TECH001', '前端开发组', 1, '北京总部A座10层', 1500000.00, '负责前端页面开发'), +('TECH002', '后端开发组', 1, '北京总部A座11层', 2000000.00, '负责后端服务开发'), +('TECH003', '测试组', 1, '北京总部A座12层', 800000.00, '负责产品质量测试'), +('SALES', '销售部', NULL, '北京总部B座', 3000000.00, '负责产品销售和客户维护'), +('SALES001', '华北销售组', 5, '北京总部B座5层', 1000000.00, '负责华北地区销售'), +('SALES002', '华南销售组', 5, '深圳分公司', 1200000.00, '负责华南地区销售'), +('HR', '人力资源部', NULL, '北京总部C座', 1000000.00, '负责人力资源管理'), +('FINANCE', '财务部', NULL, '北京总部C座', 800000.00, '负责财务管理和成本控制'), +('ADMIN', '行政部', NULL, '北京总部C座', 600000.00, '负责行政后勤管理'); + +-- 插入员工数据 +INSERT INTO employees (employee_code, name, gender, birth_date, hire_date, department_id, position, salary, manager_id, email, phone, address, status) VALUES +('EMP001', '张总', 'M', '1975-03-15', '2015-01-01', 1, 'CTO', 50000.00, NULL, 'zhang.cto@company.com', '13800001001', '北京市朝阳区', 'active'), +('EMP002', '李经理', 'F', '1982-07-20', '2018-03-15', 2, '前端技术经理', 25000.00, 1, 'li.frontend@company.com', '13800001002', '北京市海淀区', 'active'), +('EMP003', '王经理', 'M', '1980-11-08', '2017-09-01', 3, '后端技术经理', 28000.00, 1, 'wang.backend@company.com', '13800001003', '北京市西城区', 'active'), +('EMP004', '赵经理', 'F', '1985-05-12', '2019-06-01', 4, '测试经理', 22000.00, 1, 'zhao.test@company.com', '13800001004', '北京市东城区', 'active'), +('EMP005', '钱总监', 'M', '1978-09-25', '2016-08-15', 5, '销售总监', 35000.00, NULL, 'qian.sales@company.com', '13800001005', '北京市丰台区', 'active'), +('EMP006', '孙工程师', 'M', '1990-12-03', '2020-04-20', 2, '高级前端工程师', 18000.00, 2, 'sun.fe@company.com', '13800001006', '北京市昌平区', 'active'), +('EMP007', '周工程师', 'F', '1992-02-14', '2021-07-10', 2, '前端工程师', 15000.00, 2, 'zhou.fe@company.com', '13800001007', '北京市通州区', 'active'), +('EMP008', '吴工程师', 'M', '1988-06-30', '2019-11-25', 3, '高级后端工程师', 20000.00, 3, 'wu.be@company.com', '13800001008', '北京市房山区', 'active'), +('EMP009', '郑工程师', 'F', '1991-04-18', '2020-09-15', 3, '后端工程师', 16000.00, 3, 'zheng.be@company.com', '13800001009', '北京市大兴区', 'active'), +('EMP010', '刘测试', 'M', '1993-08-22', '2021-12-01', 4, '高级测试工程师', 16000.00, 4, 'liu.test@company.com', '13800001010', '北京市密云区', 'active'), +('EMP011', '陈销售', 'F', '1987-10-05', '2018-02-28', 6, '销售经理', 20000.00, 5, 'chen.sales@company.com', '13800001011', '北京市怀柔区', 'active'), +('EMP012', '林销售', 'M', '1989-12-16', '2019-05-20', 7, '销售经理', 22000.00, 5, 'lin.sales@company.com', '13800001012', '深圳市南山区', 'active'); + +-- 插入项目数据 +INSERT INTO projects (project_code, project_name, description, start_date, end_date, budget, actual_cost, status, priority, client, manager_id) VALUES +('PRJ001', '电商平台开发', '为客户开发一个完整的电商平台系统', '2024-01-15', '2024-08-15', 2000000.00, 800000.00, 'active', 'high', '阿里巴巴', 3), +('PRJ002', '移动APP开发', '开发iOS和Android移动应用', '2024-02-01', '2024-07-01', 1500000.00, 600000.00, 'active', 'medium', '腾讯', 2), +('PRJ003', '数据分析系统', '构建企业级数据分析和报表系统', '2024-03-01', '2024-09-30', 1800000.00, 300000.00, 'active', 'high', '百度', 3), +('PRJ004', '官网重构', '重新设计和开发公司官方网站', '2024-01-01', '2024-04-30', 500000.00, 450000.00, 'completed', 'medium', '字节跳动', 2), +('PRJ005', '内部管理系统', '开发HR和财务内部管理系统', '2024-04-01', '2024-10-31', 1200000.00, 200000.00, 'planning', 'low', '内部项目', 3); + +-- 插入员工项目关联数据 +INSERT INTO employee_projects (emp_id, project_id, role, allocation_percentage, start_date, end_date, hourly_rate) VALUES +(2, 2, '项目经理', 100.00, '2024-02-01', NULL, 250.00), +(2, 4, '项目经理', 80.00, '2024-01-01', '2024-04-30', 250.00), +(3, 1, '技术负责人', 100.00, '2024-01-15', NULL, 280.00), +(3, 3, '技术负责人', 70.00, '2024-03-01', NULL, 280.00), +(3, 5, '技术顾问', 30.00, '2024-04-01', NULL, 280.00), +(6, 2, '高级前端开发', 100.00, '2024-02-01', NULL, 180.00), +(6, 4, '前端开发', 100.00, '2024-01-01', '2024-04-30', 180.00), +(7, 2, '前端开发', 100.00, '2024-02-15', NULL, 150.00), +(8, 1, '高级后端开发', 100.00, '2024-01-15', NULL, 200.00), +(8, 3, '后端开发', 50.00, '2024-03-01', NULL, 200.00), +(9, 1, '后端开发', 80.00, '2024-02-01', NULL, 160.00), +(9, 5, '后端开发', 60.00, '2024-04-01', NULL, 160.00), +(10, 1, '测试工程师', 70.00, '2024-02-15', NULL, 160.00), +(10, 2, '测试工程师', 80.00, '2024-03-01', NULL, 160.00), +(10, 3, '测试工程师', 50.00, '2024-03-15', NULL, 160.00); + +-- 插入考勤数据(最近一周) +INSERT INTO attendance (emp_id, work_date, check_in_time, check_out_time, break_hours, work_hours, overtime_hours, status) VALUES +-- 张总考勤 +(1, '2024-01-15', '09:00:00', '18:30:00', 1.00, 8.50, 0.50, 'present'), +(1, '2024-01-16', '09:15:00', '19:00:00', 1.00, 8.75, 0.75, 'late'), +(1, '2024-01-17', '08:45:00', '18:00:00', 1.00, 8.25, 0.00, 'present'), +(1, '2024-01-18', '09:00:00', '20:00:00', 1.00, 10.00, 2.00, 'present'), +(1, '2024-01-19', '09:00:00', '18:00:00', 1.00, 8.00, 0.00, 'present'), +-- 李经理考勤 +(2, '2024-01-15', '09:30:00', '18:00:00', 1.00, 7.50, 0.00, 'late'), +(2, '2024-01-16', '09:00:00', '19:30:00', 1.00, 9.50, 1.50, 'present'), +(2, '2024-01-17', '09:00:00', '18:00:00', 1.00, 8.00, 0.00, 'present'), +(2, '2024-01-18', NULL, NULL, 0.00, 0.00, 0.00, 'absent'), +(2, '2024-01-19', '09:00:00', '17:30:00', 1.00, 7.50, 0.00, 'early_leave'), +-- 王经理考勤 +(3, '2024-01-15', '08:50:00', '19:00:00', 1.00, 9.17, 1.17, 'present'), +(3, '2024-01-16', '09:00:00', '21:00:00', 1.00, 11.00, 3.00, 'present'), +(3, '2024-01-17', '09:00:00', '18:00:00', 1.00, 8.00, 0.00, 'present'), +(3, '2024-01-18', '08:45:00', '18:30:00', 1.00, 8.75, 0.75, 'present'), +(3, '2024-01-19', '09:00:00', '18:00:00', 1.00, 8.00, 0.00, 'present'); +``` + +### 1. INSERT 语句详解 + +```sql +-- 基本插入 +INSERT INTO departments (dept_code, dept_name, location) +VALUES ('IT', 'IT部门', '3楼'); + +-- 插入多行 +INSERT INTO departments (dept_code, dept_name, location) VALUES +('MKT', '市场部', '2楼'), +('OPS', '运营部', '4楼'); + +-- 插入查询结果 +INSERT INTO departments (dept_code, dept_name, parent_id) +SELECT CONCAT('SUB_', dept_code), CONCAT(dept_name, '分部'), dept_id +FROM departments +WHERE parent_id IS NULL; + +-- 插入时忽略重复 +INSERT IGNORE INTO departments (dept_code, dept_name) +VALUES ('TECH', '技术部门'); + +-- 插入时更新重复 +INSERT INTO departments (dept_code, dept_name, budget) +VALUES ('TECH', '技术部', 6000000.00) +ON DUPLICATE KEY UPDATE + dept_name = VALUES(dept_name), + budget = VALUES(budget), + updated_at = CURRENT_TIMESTAMP; + +-- 替换插入 +REPLACE INTO departments (dept_id, dept_code, dept_name) +VALUES (1, 'TECH', '技术研发部'); +``` + +### 2. UPDATE 语句详解 + +```sql +-- 基本更新 +UPDATE employees +SET salary = salary * 1.1 +WHERE department_id = 1; + +-- 多表更新 +UPDATE employees e +JOIN departments d ON e.department_id = d.dept_id +SET e.salary = e.salary * 1.05 +WHERE d.dept_name LIKE '%技术%'; + +-- 条件更新 +UPDATE employees +SET + salary = CASE + WHEN position LIKE '%经理%' THEN salary * 1.15 + WHEN position LIKE '%高级%' THEN salary * 1.10 + ELSE salary * 1.05 + END, + updated_at = CURRENT_TIMESTAMP +WHERE status = 'active'; + +-- 使用子查询更新 +UPDATE employees +SET manager_id = ( + SELECT emp_id + FROM (SELECT emp_id FROM employees WHERE position = 'CTO') AS mgr +) +WHERE department_id IN (1, 2, 3, 4); + +-- 安全更新(避免全表更新) +SET SQL_SAFE_UPDATES = 1; +UPDATE employees SET salary = 15000 WHERE emp_id = 10; +``` + +### 3. DELETE 语句详解 + +```sql +-- 基本删除 +DELETE FROM employees WHERE status = 'terminated'; + +-- 多表删除 +DELETE e +FROM employees e +JOIN departments d ON e.department_id = d.dept_id +WHERE d.dept_name = '已撤销部门'; + +-- 使用子查询删除 +DELETE FROM employee_projects +WHERE project_id IN ( + SELECT project_id + FROM projects + WHERE status = 'cancelled' +); + +-- 删除重复记录(保留最新的) +DELETE e1 FROM employees e1 +INNER JOIN employees e2 +WHERE e1.email = e2.email + AND e1.emp_id < e2.emp_id; + +-- 清空表(比DELETE快) +TRUNCATE TABLE attendance; +``` + +## 数据查询语言 DQL + +### 1. 基础查询 + +```sql +-- 查询所有数据 +SELECT * FROM employees; + +-- 查询指定列 +SELECT name, position, salary FROM employees; + +-- 使用别名 +SELECT + name AS '员工姓名', + position AS '职位', + salary AS '薪资', + CONCAT(name, ' - ', position) AS '员工信息' +FROM employees; + +-- 去重查询 +SELECT DISTINCT department_id FROM employees; +SELECT DISTINCT department_id, position FROM employees; + +-- 限制结果数量 +SELECT * FROM employees LIMIT 5; +SELECT * FROM employees LIMIT 5, 10; -- 跳过5条,取10条 +``` + +### 2. 条件查询 + +```sql +-- 基本WHERE条件 +SELECT * FROM employees WHERE salary > 20000; +SELECT * FROM employees WHERE department_id = 1; +SELECT * FROM employees WHERE status = 'active'; + +-- 逻辑运算符 +SELECT * FROM employees +WHERE salary > 15000 AND department_id IN (1, 2, 3); + +SELECT * FROM employees +WHERE position LIKE '%经理%' OR salary > 25000; + +SELECT * FROM employees +WHERE NOT (status = 'terminated' OR hire_date < '2020-01-01'); + +-- 范围查询 +SELECT * FROM employees WHERE salary BETWEEN 15000 AND 25000; +SELECT * FROM employees WHERE hire_date BETWEEN '2020-01-01' AND '2023-12-31'; + +-- 模糊查询 +SELECT * FROM employees WHERE name LIKE '张%'; -- 以张开头 +SELECT * FROM employees WHERE name LIKE '%工程师'; -- 以工程师结尾 +SELECT * FROM employees WHERE email LIKE '%@company.com'; +SELECT * FROM employees WHERE phone LIKE '138%'; + +-- NULL值查询 +SELECT * FROM employees WHERE manager_id IS NULL; +SELECT * FROM employees WHERE end_date IS NOT NULL; + +-- IN查询 +SELECT * FROM employees WHERE department_id IN (1, 2, 3); +SELECT * FROM employees WHERE position IN ('CTO', '总监', '经理'); +``` + +### 3. 排序和分组 + +```sql +-- 排序 +SELECT * FROM employees ORDER BY salary DESC; +SELECT * FROM employees ORDER BY department_id, salary DESC; +SELECT * FROM employees ORDER BY hire_date, name; + +-- 分组统计 +SELECT department_id, COUNT(*) as emp_count +FROM employees +GROUP BY department_id; + +SELECT + department_id, + COUNT(*) as emp_count, + AVG(salary) as avg_salary, + MAX(salary) as max_salary, + MIN(salary) as min_salary +FROM employees +GROUP BY department_id; + +-- HAVING过滤 +SELECT department_id, COUNT(*) as emp_count +FROM employees +GROUP BY department_id +HAVING COUNT(*) > 2; + +-- 复杂分组 +SELECT + YEAR(hire_date) as hire_year, + department_id, + COUNT(*) as emp_count +FROM employees +GROUP BY YEAR(hire_date), department_id +ORDER BY hire_year, department_id; +``` + +## 复杂查询操作 + +### 1. 联表查询 + +```sql +-- 内连接(INNER JOIN) +SELECT + e.name, + e.position, + e.salary, + d.dept_name +FROM employees e +INNER JOIN departments d ON e.department_id = d.dept_id; + +-- 左连接(LEFT JOIN) +SELECT + d.dept_name, + COUNT(e.emp_id) as emp_count +FROM departments d +LEFT JOIN employees e ON d.dept_id = e.department_id +GROUP BY d.dept_id, d.dept_name; + +-- 右连接(RIGHT JOIN) +SELECT + e.name, + d.dept_name +FROM employees e +RIGHT JOIN departments d ON e.department_id = d.dept_id; + +-- 多表连接 +SELECT + e.name as employee_name, + d.dept_name, + p.project_name, + ep.role, + ep.allocation_percentage +FROM employees e +JOIN departments d ON e.department_id = d.dept_id +JOIN employee_projects ep ON e.emp_id = ep.emp_id +JOIN projects p ON ep.project_id = p.project_id +WHERE e.status = 'active'; + +-- 自连接(查询员工及其上级) +SELECT + e.name as employee, + e.position, + m.name as manager +FROM employees e +LEFT JOIN employees m ON e.manager_id = m.emp_id; +``` + +### 2. 子查询 + +```sql +-- 标量子查询 +SELECT name, salary, + (SELECT AVG(salary) FROM employees) as avg_salary, + salary - (SELECT AVG(salary) FROM employees) as diff_from_avg +FROM employees; + +-- 列子查询(IN/NOT IN) +SELECT * FROM employees +WHERE department_id IN ( + SELECT dept_id FROM departments + WHERE dept_name LIKE '%技术%' +); + +-- 行子查询 +SELECT * FROM employees +WHERE (department_id, position) IN ( + SELECT department_id, position + FROM employees + WHERE salary = (SELECT MAX(salary) FROM employees) +); + +-- EXISTS子查询 +SELECT * FROM departments d +WHERE EXISTS ( + SELECT 1 FROM employees e + WHERE e.department_id = d.dept_id +); + +-- 相关子查询 +SELECT e1.name, e1.salary, e1.department_id +FROM employees e1 +WHERE e1.salary > ( + SELECT AVG(e2.salary) + FROM employees e2 + WHERE e2.department_id = e1.department_id +); +``` + +### 3. 窗口函数 + +```sql +-- ROW_NUMBER() - 行号 +SELECT + name, + department_id, + salary, + ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) as salary_rank +FROM employees; + +-- RANK() 和 DENSE_RANK() - 排名 +SELECT + name, + salary, + RANK() OVER (ORDER BY salary DESC) as rank_with_gaps, + DENSE_RANK() OVER (ORDER BY salary DESC) as rank_dense +FROM employees; + +-- NTILE() - 分组 +SELECT + name, + salary, + NTILE(4) OVER (ORDER BY salary DESC) as salary_quartile +FROM employees; + +-- LAG() 和 LEAD() - 偏移 +SELECT + name, + hire_date, + LAG(hire_date, 1) OVER (ORDER BY hire_date) as prev_hire_date, + LEAD(hire_date, 1) OVER (ORDER BY hire_date) as next_hire_date +FROM employees; + +-- FIRST_VALUE() 和 LAST_VALUE() +SELECT + name, + department_id, + salary, + FIRST_VALUE(name) OVER (PARTITION BY department_id ORDER BY salary DESC) as highest_paid, + LAST_VALUE(name) OVER ( + PARTITION BY department_id + ORDER BY salary DESC + RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) as lowest_paid +FROM employees; + +-- 累计计算 +SELECT + name, + salary, + SUM(salary) OVER (ORDER BY emp_id) as running_total, + AVG(salary) OVER (ORDER BY emp_id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as moving_avg +FROM employees; +``` + +### 4. 通用表表达式 (CTE) + +```sql +-- 基本CTE +WITH dept_stats AS ( + SELECT + department_id, + COUNT(*) as emp_count, + AVG(salary) as avg_salary + FROM employees + GROUP BY department_id +) +SELECT + d.dept_name, + ds.emp_count, + ds.avg_salary +FROM dept_stats ds +JOIN departments d ON ds.department_id = d.dept_id; + +-- 递归CTE(组织架构) +WITH RECURSIVE org_hierarchy AS ( + -- 基础查询:顶层管理者 + SELECT emp_id, name, manager_id, position, 0 as level + FROM employees + WHERE manager_id IS NULL + + UNION ALL + + -- 递归查询:下级员工 + SELECT e.emp_id, e.name, e.manager_id, e.position, oh.level + 1 + FROM employees e + INNER JOIN org_hierarchy oh ON e.manager_id = oh.emp_id +) +SELECT + CONCAT(REPEAT(' ', level), name) as hierarchy, + position, + level +FROM org_hierarchy +ORDER BY level, name; + +-- 多个CTE +WITH +high_performers AS ( + SELECT emp_id, name, salary + FROM employees + WHERE salary > (SELECT AVG(salary) FROM employees) +), +project_leaders AS ( + SELECT DISTINCT emp_id + FROM employee_projects + WHERE role LIKE '%经理%' OR role LIKE '%负责人%' +) +SELECT hp.name, hp.salary +FROM high_performers hp +JOIN project_leaders pl ON hp.emp_id = pl.emp_id; +``` + +### 5. 联合查询 + +```sql +-- UNION(去重) +SELECT name, 'Employee' as type FROM employees +UNION +SELECT dept_name, 'Department' as type FROM departments; + +-- UNION ALL(不去重) +SELECT emp_id as id, name FROM employees +UNION ALL +SELECT dept_id as id, dept_name as name FROM departments; + +-- 复杂UNION查询 +SELECT + 'Active Projects' as category, + COUNT(*) as count, + SUM(budget) as total_budget +FROM projects +WHERE status = 'active' + +UNION ALL + +SELECT + 'Completed Projects' as category, + COUNT(*) as count, + SUM(budget) as total_budget +FROM projects +WHERE status = 'completed' + +UNION ALL + +SELECT + 'All Projects' as category, + COUNT(*) as count, + SUM(budget) as total_budget +FROM projects; +``` + +## 索引和约束 + +### 1. 索引管理 + +```sql +-- 查看索引 +SHOW INDEX FROM employees; +SHOW INDEX FROM departments; + +-- 创建索引 +CREATE INDEX idx_emp_name ON employees(name); +CREATE INDEX idx_emp_salary_desc ON employees(salary DESC); +CREATE INDEX idx_emp_dept_salary ON employees(department_id, salary); + +-- 创建唯一索引 +CREATE UNIQUE INDEX uk_emp_email ON employees(email); + +-- 创建全文索引(用于文本搜索) +ALTER TABLE projects ADD FULLTEXT(description); + +-- 使用全文索引搜索 +SELECT * FROM projects +WHERE MATCH(description) AGAINST('系统 开发' IN NATURAL LANGUAGE MODE); + +-- 删除索引 +DROP INDEX idx_emp_name ON employees; +ALTER TABLE employees DROP INDEX idx_emp_salary_desc; + +-- 分析索引使用情况 +EXPLAIN SELECT * FROM employees WHERE name = '张总'; +EXPLAIN SELECT * FROM employees WHERE salary > 20000 AND department_id = 1; +``` + +### 2. 约束管理 + +```sql +-- 主键约束 +ALTER TABLE employees ADD PRIMARY KEY (emp_id); +ALTER TABLE employees DROP PRIMARY KEY; + +-- 外键约束 +ALTER TABLE employees +ADD CONSTRAINT fk_emp_dept +FOREIGN KEY (department_id) REFERENCES departments(dept_id) +ON UPDATE CASCADE ON DELETE SET NULL; + +-- 唯一约束 +ALTER TABLE employees ADD CONSTRAINT uk_emp_code UNIQUE (employee_code); +ALTER TABLE employees ADD CONSTRAINT uk_emp_email UNIQUE (email); + +-- 检查约束(MySQL 8.0.16+) +ALTER TABLE employees +ADD CONSTRAINT chk_salary_positive CHECK (salary > 0); + +ALTER TABLE employees +ADD CONSTRAINT chk_hire_date CHECK (hire_date <= CURDATE()); + +-- 删除约束 +ALTER TABLE employees DROP CONSTRAINT fk_emp_dept; +ALTER TABLE employees DROP CONSTRAINT uk_emp_code; + +-- 查看约束 +SELECT + CONSTRAINT_NAME, + CONSTRAINT_TYPE, + TABLE_NAME +FROM information_schema.TABLE_CONSTRAINTS +WHERE TABLE_SCHEMA = 'company_db'; +``` + +## 存储过程和函数 + +### 1. 存储过程 + +```sql +-- 创建简单存储过程 +DELIMITER // +CREATE PROCEDURE GetEmployeesByDept(IN dept_id INT) +BEGIN + SELECT + name, + position, + salary, + hire_date + FROM employees + WHERE department_id = dept_id + AND status = 'active' + ORDER BY salary DESC; +END // +DELIMITER ; + +-- 调用存储过程 +CALL GetEmployeesByDept(1); + +-- 复杂存储过程(带输出参数) +DELIMITER // +CREATE PROCEDURE GetDeptStatistics( + IN dept_id INT, + OUT emp_count INT, + OUT avg_salary DECIMAL(10,2), + OUT total_salary DECIMAL(12,2) +) +BEGIN + SELECT + COUNT(*), + ROUND(AVG(salary), 2), + SUM(salary) + INTO emp_count, avg_salary, total_salary + FROM employees + WHERE department_id = dept_id AND status = 'active'; +END // +DELIMITER ; + +-- 调用带输出参数的存储过程 +CALL GetDeptStatistics(1, @count, @avg, @total); +SELECT @count as employee_count, @avg as average_salary, @total as total_salary; + +-- 复杂业务逻辑存储过程 +DELIMITER // +CREATE PROCEDURE CalculateBonus(IN year INT) +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE emp_id INT; + DECLARE emp_salary DECIMAL(10,2); + DECLARE project_count INT; + DECLARE bonus_amount DECIMAL(8,2); + + -- 声明游标 + DECLARE emp_cursor CURSOR FOR + SELECT e.emp_id, e.salary, COUNT(ep.project_id) + FROM employees e + LEFT JOIN employee_projects ep ON e.emp_id = ep.emp_id + WHERE e.status = 'active' + GROUP BY e.emp_id, e.salary; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + -- 创建临时表存储结果 + CREATE TEMPORARY TABLE IF NOT EXISTS temp_bonus ( + emp_id INT, + bonus_amount DECIMAL(8,2) + ); + + OPEN emp_cursor; + + bonus_loop: LOOP + FETCH emp_cursor INTO emp_id, emp_salary, project_count; + IF done THEN + LEAVE bonus_loop; + END IF; + + -- 计算奖金逻辑 + SET bonus_amount = emp_salary * 0.1; -- 基础奖金10% + + IF project_count > 3 THEN + SET bonus_amount = bonus_amount * 1.5; -- 项目多奖励50% + ELSEIF project_count > 1 THEN + SET bonus_amount = bonus_amount * 1.2; -- 项目适中奖励20% + END IF; + + INSERT INTO temp_bonus VALUES (emp_id, bonus_amount); + END LOOP; + + CLOSE emp_cursor; + + -- 返回结果 + SELECT + e.name, + e.position, + e.salary, + tb.bonus_amount + FROM temp_bonus tb + JOIN employees e ON tb.emp_id = e.emp_id + ORDER BY tb.bonus_amount DESC; + + DROP TEMPORARY TABLE temp_bonus; +END // +DELIMITER ; + +-- 调用复杂存储过程 +CALL CalculateBonus(2024); +``` + +### 2. 函数 + +```sql +-- 创建函数 +DELIMITER // +CREATE FUNCTION GetEmployeeLevel(salary DECIMAL(10,2)) +RETURNS VARCHAR(20) +READS SQL DATA +DETERMINISTIC +BEGIN + DECLARE level_name VARCHAR(20); + + CASE + WHEN salary >= 30000 THEN SET level_name = '高级'; + WHEN salary >= 20000 THEN SET level_name = '中级'; + WHEN salary >= 15000 THEN SET level_name = '初级'; + ELSE SET level_name = '实习'; + END CASE; + + RETURN level_name; +END // +DELIMITER ; + +-- 使用函数 +SELECT + name, + salary, + GetEmployeeLevel(salary) as employee_level +FROM employees; + +-- 复杂函数示例 +DELIMITER // +CREATE FUNCTION CalculateWorkingDays(start_date DATE, end_date DATE) +RETURNS INT +READS SQL DATA +DETERMINISTIC +BEGIN + DECLARE working_days INT DEFAULT 0; + DECLARE current_date DATE; + DECLARE day_of_week INT; + + SET current_date = start_date; + + WHILE current_date <= end_date DO + SET day_of_week = DAYOFWEEK(current_date); + -- 周一到周五为工作日(2-6) + IF day_of_week BETWEEN 2 AND 6 THEN + SET working_days = working_days + 1; + END IF; + SET current_date = DATE_ADD(current_date, INTERVAL 1 DAY); + END WHILE; + + RETURN working_days; +END // +DELIMITER ; + +-- 使用复杂函数 +SELECT + project_name, + start_date, + end_date, + CalculateWorkingDays(start_date, IFNULL(end_date, CURDATE())) as working_days +FROM projects; + +-- 删除函数和存储过程 +DROP FUNCTION IF EXISTS GetEmployeeLevel; +DROP PROCEDURE IF EXISTS GetEmployeesByDept; + +-- 查看存储过程和函数 +SHOW PROCEDURE STATUS WHERE Db = 'company_db'; +SHOW FUNCTION STATUS WHERE Db = 'company_db'; +``` + +## 实际项目案例 + +### 案例1:员工绩效分析系统 + +```sql +-- 创建员工绩效评估视图 +CREATE VIEW employee_performance_view AS +SELECT + e.emp_id, + e.name, + e.position, + d.dept_name, + e.salary, + -- 项目参与情况 + COUNT(DISTINCT ep.project_id) as project_count, + -- 工作时长统计 + AVG(a.work_hours) as avg_daily_hours, + SUM(a.overtime_hours) as total_overtime, + -- 出勤率 + COUNT(CASE WHEN a.status = 'present' THEN 1 END) / COUNT(a.work_date) * 100 as attendance_rate, + -- 薪资水平 + e.salary / AVG(e2.salary) * 100 as salary_percentile +FROM employees e +LEFT JOIN departments d ON e.department_id = d.dept_id +LEFT JOIN employee_projects ep ON e.emp_id = ep.emp_id +LEFT JOIN attendance a ON e.emp_id = a.emp_id +LEFT JOIN employees e2 ON e.department_id = e2.department_id +WHERE e.status = 'active' +GROUP BY e.emp_id, e.name, e.position, d.dept_name, e.salary; + +-- 查询高绩效员工 +SELECT * FROM employee_performance_view +WHERE project_count >= 2 + AND avg_daily_hours >= 8 + AND attendance_rate >= 95 + AND salary_percentile >= 110 +ORDER BY salary_percentile DESC; +``` + +### 案例2:项目成本分析 + +```sql +-- 项目成本分析查询 +SELECT + p.project_name, + p.budget, + p.actual_cost, + -- 人力成本计算 + SUM( + ep.hourly_rate * + DATEDIFF( + IFNULL(ep.end_date, IFNULL(p.end_date, CURDATE())), + ep.start_date + ) * 8 * -- 假设每天8小时 + (ep.allocation_percentage / 100) + ) as estimated_labor_cost, + -- 预算使用率 + p.actual_cost / p.budget * 100 as budget_utilization, + -- 项目进度(简化计算) + CASE + WHEN p.status = 'completed' THEN 100 + WHEN p.status = 'active' THEN + DATEDIFF(CURDATE(), p.start_date) / + DATEDIFF(IFNULL(p.end_date, CURDATE()), p.start_date) * 100 + ELSE 0 + END as progress_percentage +FROM projects p +LEFT JOIN employee_projects ep ON p.project_id = ep.project_id +GROUP BY p.project_id, p.project_name, p.budget, p.actual_cost, p.status, p.start_date, p.end_date +HAVING estimated_labor_cost > 0; +``` + +### 案例3:部门效率分析 + +```sql +-- 部门效率综合分析 +WITH dept_metrics AS ( + SELECT + d.dept_id, + d.dept_name, + d.budget, + COUNT(DISTINCT e.emp_id) as emp_count, + AVG(e.salary) as avg_salary, + SUM(e.salary) as total_salary, + COUNT(DISTINCT ep.project_id) as project_count, + AVG(a.work_hours) as avg_work_hours, + AVG(CASE WHEN a.status = 'present' THEN 1 ELSE 0 END) * 100 as attendance_rate + FROM departments d + LEFT JOIN employees e ON d.dept_id = e.department_id AND e.status = 'active' + LEFT JOIN employee_projects ep ON e.emp_id = ep.emp_id + LEFT JOIN attendance a ON e.emp_id = a.emp_id + GROUP BY d.dept_id, d.dept_name, d.budget +), +dept_rankings AS ( + SELECT *, + RANK() OVER (ORDER BY project_count DESC) as project_rank, + RANK() OVER (ORDER BY attendance_rate DESC) as attendance_rank, + RANK() OVER (ORDER BY avg_work_hours DESC) as productivity_rank + FROM dept_metrics + WHERE emp_count > 0 +) +SELECT + dept_name, + emp_count, + project_count, + ROUND(avg_salary, 2) as avg_salary, + ROUND(attendance_rate, 2) as attendance_rate, + ROUND(avg_work_hours, 2) as avg_work_hours, + project_rank, + attendance_rank, + productivity_rank, + -- 综合效率评分 + ROUND((project_rank + attendance_rank + productivity_rank) / 3, 2) as efficiency_score +FROM dept_rankings +ORDER BY efficiency_score; +``` + +## 性能优化和最佳实践 + +### 1. 查询优化 + +```sql +-- 使用EXPLAIN分析查询计划 +EXPLAIN FORMAT=JSON +SELECT e.name, d.dept_name, p.project_name +FROM employees e +JOIN departments d ON e.department_id = d.dept_id +JOIN employee_projects ep ON e.emp_id = ep.emp_id +JOIN projects p ON ep.project_id = p.project_id +WHERE e.salary > 20000; + +-- 优化前:全表扫描 +SELECT * FROM employees WHERE YEAR(hire_date) = 2024; + +-- 优化后:使用索引友好的条件 +SELECT * FROM employees +WHERE hire_date >= '2024-01-01' AND hire_date < '2025-01-01'; + +-- 优化前:函数使用导致无法使用索引 +SELECT * FROM employees WHERE UPPER(name) = 'ZHANG'; + +-- 优化后:避免在WHERE中使用函数 +SELECT * FROM employees WHERE name = 'zhang' COLLATE utf8mb4_general_ci; +``` + +### 2. 索引优化策略 + +```sql +-- 创建复合索引时考虑最左前缀原则 +CREATE INDEX idx_emp_dept_salary_hire ON employees(department_id, salary, hire_date); + +-- 以下查询可以使用上述索引 +SELECT * FROM employees WHERE department_id = 1; +SELECT * FROM employees WHERE department_id = 1 AND salary > 15000; +SELECT * FROM employees WHERE department_id = 1 AND salary > 15000 AND hire_date > '2020-01-01'; + +-- 以下查询无法使用上述索引 +SELECT * FROM employees WHERE salary > 15000; -- 跳过了department_id +SELECT * FROM employees WHERE hire_date > '2020-01-01'; -- 跳过了department_id和salary + +-- 覆盖索引优化 +CREATE INDEX idx_emp_covering ON employees(department_id, name, salary); + +-- 该查询只需要访问索引,不需要回表 +SELECT name, salary FROM employees WHERE department_id = 1; +``` + +### 3. 分区表优化 + +```sql +-- 按日期分区的考勤表 +CREATE TABLE attendance_partitioned ( + id INT PRIMARY KEY AUTO_INCREMENT, + emp_id INT NOT NULL, + work_date DATE NOT NULL, + check_in_time TIME, + check_out_time TIME, + work_hours DECIMAL(4,2), + status ENUM('present', 'absent', 'late', 'early_leave', 'holiday') DEFAULT 'present', + INDEX idx_emp_date (emp_id, work_date) +) ENGINE=InnoDB +PARTITION BY RANGE (YEAR(work_date)) ( + PARTITION p2022 VALUES LESS THAN (2023), + PARTITION p2023 VALUES LESS THAN (2024), + PARTITION p2024 VALUES LESS THAN (2025), + PARTITION p_future VALUES LESS THAN MAXVALUE +); + +-- 查询时自动使用分区剪枝 +SELECT * FROM attendance_partitioned +WHERE work_date BETWEEN '2024-01-01' AND '2024-12-31'; +``` + +### 4. 查询缓存和性能监控 + +```sql +-- 查看慢查询 +SELECT + sql_text, + exec_count, + avg_timer_wait/1000000000000 as avg_time_sec, + sum_rows_examined/exec_count as avg_rows_examined +FROM performance_schema.events_statements_summary_by_digest +WHERE avg_timer_wait > 1000000000000 -- 超过1秒的查询 +ORDER BY avg_timer_wait DESC +LIMIT 10; + +-- 查看表的访问统计 +SELECT + object_schema, + object_name, + count_star as total_queries, + sum_timer_wait/1000000000000 as total_time_sec +FROM performance_schema.table_io_waits_summary_by_table +WHERE object_schema = 'company_db' +ORDER BY sum_timer_wait DESC; + +-- 查看索引使用情况 +SELECT + object_schema, + object_name, + index_name, + count_star as usage_count +FROM performance_schema.table_io_waits_summary_by_index_usage +WHERE object_schema = 'company_db' +ORDER BY count_star DESC; +``` + +### 5. 最佳实践总结 + +```sql +-- ✅ 良好的SQL编写习惯 + +-- 1. 明确指定需要的列,避免SELECT * +SELECT emp_id, name, salary FROM employees WHERE department_id = 1; + +-- 2. 合理使用索引 +CREATE INDEX idx_emp_dept_status ON employees(department_id, status); +SELECT * FROM employees WHERE department_id = 1 AND status = 'active'; + +-- 3. 避免隐式类型转换 +SELECT * FROM employees WHERE emp_id = 123; -- 而不是 emp_id = '123' + +-- 4. 使用LIMIT限制结果集 +SELECT * FROM employees ORDER BY hire_date DESC LIMIT 20; + +-- 5. 合理使用JOIN替代子查询 +-- 推荐 +SELECT e.name, d.dept_name +FROM employees e +JOIN departments d ON e.department_id = d.dept_id; + +-- 而不是 +SELECT name, (SELECT dept_name FROM departments WHERE dept_id = e.department_id) +FROM employees e; + +-- 6. 批量操作优化 +-- 批量插入 +INSERT INTO attendance (emp_id, work_date, work_hours) VALUES +(1, '2024-01-01', 8.0), +(1, '2024-01-02', 8.5), +(1, '2024-01-03', 8.0); + +-- 批量更新 +UPDATE employees +SET salary = salary * 1.1 +WHERE department_id IN (1, 2, 3); + +-- 7. 事务使用 +START TRANSACTION; +INSERT INTO projects (project_name, start_date) VALUES ('新项目', '2024-01-01'); +SET @project_id = LAST_INSERT_ID(); +INSERT INTO employee_projects (emp_id, project_id, role) VALUES (1, @project_id, '项目经理'); +COMMIT; +``` + +--- + +**总结:** + +本文档涵盖了MySQL常用SQL语法的完整使用指南,包括: + +1. **DDL操作**:数据库和表的创建、修改、删除 +2. **DML操作**:数据的增删改查操作 +3. **复杂查询**:联表查询、子查询、窗口函数、CTE +4. **索引约束**:性能优化的关键技术 +5. **存储过程**:复杂业务逻辑的封装 +6. **实际案例**:真实业务场景的应用 +7. **性能优化**:查询优化和最佳实践 + +通过系统学习和实践这些内容,可以掌握MySQL数据库开发和优化的核心技能。 \ No newline at end of file diff --git a/Spring三级缓存机制学习笔记.md b/Spring三级缓存机制学习笔记.md new file mode 100644 index 0000000..29b52e6 --- /dev/null +++ b/Spring三级缓存机制学习笔记.md @@ -0,0 +1,327 @@ +# Spring三级缓存机制学习笔记 + +## 📚 概述 + +Spring三级缓存是Spring框架用来解决**单例Bean循环依赖**问题的核心机制。它通过三个不同的缓存Map来存储Bean在不同生命周期阶段的状态。 + +--- + +## 🎯 学习目标 + +- [ ] 理解什么是循环依赖问题 +- [ ] 掌握三级缓存的结构和作用 +- [ ] 理解循环依赖的解决流程 +- [ ] 学会分析相关源码 + +--- + +## 🔍 核心概念 + +### 什么是循环依赖? + +当两个或多个Bean相互依赖时形成的依赖环: + +```java +@Component +public class ServiceA { + @Autowired + private ServiceB serviceB; // A依赖B +} + +@Component +public class ServiceB { + @Autowired + private ServiceA serviceA; // B依赖A +} +``` + +**问题**:如果没有特殊处理,会陷入无限循环创建对象的死循环。 + +--- + +## 🏗️ 三级缓存结构 + +Spring使用三个Map来管理Bean的不同状态: + +```java +/** 一级缓存:完整的单例Bean */ +private final Map singletonObjects = new ConcurrentHashMap<>(256); + +/** 二级缓存:早期暴露的Bean对象 */ +private final Map earlySingletonObjects = new HashMap<>(16); + +/** 三级缓存:Bean工厂对象 */ +private final Map> singletonFactories = new HashMap<>(16); +``` + +### 各级缓存详解 + +| 缓存级别 | 名称 | 作用 | 存储内容 | +|---------|------|------|---------| +| **一级缓存** | singletonObjects | 成品仓库 | 完全初始化完成的Bean | +| **二级缓存** | earlySingletonObjects | 半成品仓库 | 实例化但未完成依赖注入的Bean | +| **三级缓存** | singletonFactories | 工厂仓库 | Bean的ObjectFactory,支持AOP代理 | + +--- + +## 🔄 循环依赖解决流程 + +以ServiceA ↔ ServiceB循环依赖为例: + +### 流程图 +``` +1. 创建ServiceA + ↓ +2. ServiceA实例化 → 放入三级缓存 + ↓ +3. ServiceA需要ServiceB → 开始创建ServiceB + ↓ +4. ServiceB实例化 → 放入三级缓存 + ↓ +5. ServiceB需要ServiceA → 从缓存获取ServiceA早期引用 + ↓ +6. ServiceA从三级缓存移至二级缓存 + ↓ +7. ServiceB完成创建 → 放入一级缓存 + ↓ +8. ServiceA完成创建 → 从二级缓存移至一级缓存 +``` + +### 详细步骤 + +#### 第1步:开始创建ServiceA +```java +// 1. 实例化ServiceA(构造函数) +ServiceA serviceA = new ServiceA(); + +// 2. 将ServiceA的工厂放入三级缓存 +singletonFactories.put("serviceA", () -> getEarlyBeanReference("serviceA", serviceA)); + +// 3. 开始属性注入,发现需要ServiceB +``` + +#### 第2步:开始创建ServiceB +```java +// 1. 实例化ServiceB(构造函数) +ServiceB serviceB = new ServiceB(); + +// 2. 将ServiceB的工厂放入三级缓存 +singletonFactories.put("serviceB", () -> getEarlyBeanReference("serviceB", serviceB)); + +// 3. 开始属性注入,发现需要ServiceA +``` + +#### 第3步:获取ServiceA早期引用 +```java +// 调用getSingleton("serviceA", true) +Object serviceA = singletonObjects.get("serviceA"); // 一级缓存:null +if (serviceA == null) { + serviceA = earlySingletonObjects.get("serviceA"); // 二级缓存:null + if (serviceA == null) { + ObjectFactory factory = singletonFactories.get("serviceA"); // 三级缓存:找到! + serviceA = factory.getObject(); // 获取早期引用 + earlySingletonObjects.put("serviceA", serviceA); // 移到二级缓存 + singletonFactories.remove("serviceA"); // 从三级缓存移除 + } +} +``` + +#### 第4步:完成ServiceB创建 +```java +// ServiceB获得ServiceA的早期引用,完成属性注入 +serviceB.setServiceA(serviceA); + +// ServiceB初始化完成,放入一级缓存 +singletonObjects.put("serviceB", serviceB); +``` + +#### 第5步:完成ServiceA创建 +```java +// ServiceA获得ServiceB的完整引用,完成属性注入 +serviceA.setServiceB(serviceB); + +// ServiceA初始化完成,从二级缓存移到一级缓存 +singletonObjects.put("serviceA", serviceA); +earlySingletonObjects.remove("serviceA"); +``` + +--- + +## 💡 核心源码分析 + +### getSingleton方法(核心逻辑) + +```java +/** + * 从缓存中获取单例Bean + * @param beanName Bean名称 + * @param allowEarlyReference 是否允许早期引用 + */ +protected Object getSingleton(String beanName, boolean allowEarlyReference) { + // 一级缓存获取完整Bean + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果一级缓存没有,且Bean正在创建中 + if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { + synchronized (this.singletonObjects) { + // 二级缓存获取早期Bean + singletonObject = this.earlySingletonObjects.get(beanName); + + // 如果二级缓存也没有,且允许早期引用 + if (singletonObject == null && allowEarlyReference) { + // 三级缓存获取Bean工厂 + ObjectFactory singletonFactory = this.singletonFactories.get(beanName); + if (singletonFactory != null) { + // 通过工厂创建早期引用 + singletonObject = singletonFactory.getObject(); + // 升级到二级缓存 + this.earlySingletonObjects.put(beanName, singletonObject); + // 从三级缓存移除 + this.singletonFactories.remove(beanName); + } + } + } + } + return singletonObject; +} +``` + +### addSingletonFactory方法 + +```java +/** + * 添加单例工厂到三级缓存 + */ +protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { + synchronized (this.singletonObjects) { + if (!this.singletonObjects.containsKey(beanName)) { + // 放入三级缓存 + this.singletonFactories.put(beanName, singletonFactory); + // 从二级缓存移除 + this.earlySingletonObjects.remove(beanName); + } + } +} +``` + +--- + +## 🤔 常见问题 + +### Q1: 为什么需要三级缓存?二级不够吗? + +**答:** 三级缓存主要是为了支持AOP代理: +- 如果Bean需要被代理(如@Transactional),不能直接暴露原始对象 +- 通过ObjectFactory可以在需要时创建代理对象 +- 保证循环依赖中获取的是正确的代理对象 + +### Q2: 构造器循环依赖能解决吗? + +**答:** 不能!三级缓存只能解决属性注入的循环依赖: +```java +// ❌ 构造器循环依赖 - 无法解决 +@Component +public class ServiceA { + public ServiceA(ServiceB serviceB) { } +} + +// ✅ 属性注入循环依赖 - 可以解决 +@Component +public class ServiceA { + @Autowired + private ServiceB serviceB; +} +``` + +### Q3: Prototype作用域的循环依赖呢? + +**答:** 不能解决!因为: +- Prototype每次都创建新实例 +- 没有缓存机制 +- 会导致无限递归创建 + +--- + +## 🎯 实践建议 + +### 1. 避免循环依赖的最佳实践 + +```java +// ❌ 避免:直接的双向依赖 +@Service +public class UserService { + @Autowired private OrderService orderService; +} + +@Service +public class OrderService { + @Autowired private UserService userService; +} + +// ✅ 推荐:引入第三方服务 +@Service +public class UserOrderService { + @Autowired private UserService userService; + @Autowired private OrderService orderService; + + public void processUserOrder(Long userId, Long orderId) { + // 协调两个服务的交互 + } +} +``` + +### 2. 使用@Lazy注解延迟加载 + +```java +@Service +public class ServiceA { + @Autowired + @Lazy // 延迟加载,避免循环依赖 + private ServiceB serviceB; +} +``` + +### 3. 通过ApplicationContext获取Bean + +```java +@Service +public class ServiceA implements ApplicationContextAware { + private ApplicationContext applicationContext; + + public void someMethod() { + // 运行时获取,避免循环依赖 + ServiceB serviceB = applicationContext.getBean(ServiceB.class); + } +} +``` + +--- + +## 📝 学习总结 + +### 关键点记忆 +1. **三级缓存目的**:解决单例Bean的循环依赖 +2. **核心思想**:提前暴露Bean的早期引用 +3. **适用范围**:仅限于属性注入的循环依赖 +4. **AOP支持**:通过ObjectFactory支持代理对象 + +### 面试重点 +- 能够清晰描述三级缓存的结构和作用 +- 能够完整说出循环依赖的解决流程 +- 了解哪些情况下循环依赖无法解决 +- 知道避免循环依赖的最佳实践 + +--- + +## 📚 扩展阅读 + +- Spring官方文档:Bean的生命周期 +- 源码位置:`DefaultSingletonBeanRegistry`类 +- 相关概念:Spring AOP、Bean作用域 +- 设计模式:工厂模式、单例模式 + +--- + +**创建时间**:2025-06-25 +**最后更新**:2025-06-25 +**难度等级**:⭐⭐⭐⭐☆ \ No newline at end of file diff --git a/Vue新手完全指南-基于你的项目.md b/Vue新手完全指南-基于你的项目.md new file mode 100644 index 0000000..7ed3aa3 --- /dev/null +++ b/Vue新手完全指南-基于你的项目.md @@ -0,0 +1,808 @@ +# Vue 新手完全指南 - 基于你的待办事项项目 + +## 📚 目录 +1. [Vue 基础概念](#vue-基础概念) +2. [项目结构理解](#项目结构理解) +3. [Composition API 详解](#composition-api-详解) +4. [响应式数据系统](#响应式数据系统) +5. [模板语法与指令](#模板语法与指令) +6. [事件处理](#事件处理) +7. [计算属性](#计算属性) +8. [CSS 与样式处理](#css-与样式处理) +9. [组件开发模式](#组件开发模式) +10. [构建与部署](#构建与部署) + +--- + +## Vue 基础概念 + +### 🎯 什么是Vue? +Vue.js 是一个**渐进式JavaScript框架**,用于构建用户界面。你的项目使用的是Vue 3,这是目前最新的版本。 + +**核心特点:** +- **响应式**:数据变化时,界面自动更新 +- **组件化**:将复杂界面拆分成小组件 +- **声明式**:描述"要什么结果",而不是"怎么做" + +### 🏗️ Vue应用的工作原理 +```javascript +// 你的项目入口文件:src/main.js +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') +``` + +**解释:** +1. `createApp()` - 创建Vue应用实例 +2. `App` - 根组件(你的整个应用) +3. `mount('#app')` - 挂载到HTML元素上 + +--- + +## 项目结构理解 + +### 📁 你的项目文件结构 +``` +todo-app/ +├── src/ +│ ├── App.vue # 主组件(你的核心代码) +│ ├── main.js # 应用入口 +│ └── assets/ # 静态资源 +├── public/ # 公共文件 +├── package.json # 项目配置 +└── vite.config.js # 构建工具配置 +``` + +### 📄 单文件组件 (.vue文件) +你的`App.vue`是一个**单文件组件**,包含三个部分: + +```vue + + + + + +``` + +**新手要点:** +- `