一、MySQL 连接与基础操作
1.1 连接数据库
-- 基本连接
mysql -u root -p
Enter password: ******
-- 指定主机和端口
mysql -h localhost -P 3306 -u username -p
-- 连接指定数据库
mysql -u username -p database_name
-- 使用SSL连接
mysql --ssl-ca=ca.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem -u root -p
1.2 数据库基本操作
-- 查看所有数据库
SHOW DATABASES;
-- 创建数据库
CREATE DATABASE company;
CREATE DATABASE IF NOT EXISTS company;
-- 指定字符集和排序规则
CREATE DATABASE company
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- 选择使用数据库
USE company;
-- 删除数据库
DROP DATABASE company;
DROP DATABASE IF EXISTS company;
-- 查看当前数据库
SELECT DATABASE();
-- 查看数据库创建语句
SHOW CREATE DATABASE company;
二、MySQL DDL 数据定义语言
2.1 创建表
-- 基本创建表
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
hire_date DATE DEFAULT (CURRENT_DATE),
salary DECIMAL(10,2),
department_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 带索引和注释的表
CREATE TABLE departments (
department_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID',
department_name VARCHAR(100) NOT NULL COMMENT '部门名称',
manager_id INT COMMENT '经理ID',
location VARCHAR(200) COMMENT '办公地点',
INDEX idx_department_name (department_name),
INDEX idx_manager (manager_id)
) COMMENT='部门信息表' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2 修改表结构
-- 添加列
ALTER TABLE employees ADD COLUMN phone VARCHAR(20) AFTER email;
-- 修改列
ALTER TABLE employees MODIFY COLUMN salary DECIMAL(12,2) NOT NULL DEFAULT 0;
-- 重命名列
ALTER TABLE employees CHANGE COLUMN phone mobile VARCHAR(20);
-- 删除列
ALTER TABLE employees DROP COLUMN mobile;
-- 添加索引
ALTER TABLE employees ADD INDEX idx_email (email);
ALTER TABLE employees ADD UNIQUE INDEX uk_email (email);
ALTER TABLE employees ADD FULLTEXT INDEX ft_name (first_name, last_name);
-- 删除索引
ALTER TABLE employees DROP INDEX idx_email;
-- 重命名表
RENAME TABLE employees TO staff;
ALTER TABLE staff RENAME TO employees;
2.3 删除表
-- 删除表
DROP TABLE employees;
-- 安全删除表
DROP TABLE IF EXISTS employees;
-- 清空表数据(重置自增ID)
TRUNCATE TABLE employees;
三、MySQL DML 数据操作语言
3.1 插入数据
-- 插入单条数据
INSERT INTO employees (first_name, last_name, email, salary, department_id)
VALUES ('张', '三', 'zhangsan@example.com', 5000.00, 1);
-- 插入多条数据
INSERT INTO employees (first_name, last_name, email, salary, department_id)
VALUES
('李', '四', 'lisi@example.com', 6000.00, 1),
('王', '五', 'wangwu@example.com', 7000.00, 2),
('赵', '六', 'zhaoliu@example.com', 5500.00, 3);
-- 插入时忽略错误
INSERT IGNORE INTO employees (first_name, last_name, email, salary, department_id)
VALUES ('张', '三', 'zhangsan@example.com', 5000.00, 1);
-- 插入并替换(存在则更新)
REPLACE INTO employees (id, first_name, last_name, email)
VALUES (1, '张', '三', 'zhangsan@example.com');
-- 从查询结果插入
INSERT INTO employee_backup
SELECT * FROM employees WHERE hire_date < '2020-01-01';
3.2 更新数据
-- 更新单条数据
UPDATE employees
SET salary = 5500.00, updated_at = CURRENT_TIMESTAMP
WHERE id = 1;
-- 更新多条数据
UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 1;
-- 使用子查询更新
UPDATE employees e
SET salary = (
SELECT AVG(salary)
FROM employees
WHERE department_id = e.department_id
)
WHERE department_id = 2;
-- 使用JOIN更新
UPDATE employees e
JOIN departments d ON e.department_id = d.department_id
SET e.salary = e.salary * 1.05
WHERE d.department_name = '技术部';
-- 批量更新
UPDATE employees
SET status = 'active'
WHERE id IN (1, 3, 5, 7, 9);
3.3 删除数据
-- 删除单条数据
DELETE FROM employees WHERE id = 1;
-- 删除多条数据
DELETE FROM employees WHERE department_id = 3;
-- 使用子查询删除
DELETE FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE location = '北京'
);
-- 使用JOIN删除
DELETE e
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.department_name = '测试部';
-- 清空表(比DELETE快,不可回滚)
TRUNCATE TABLE temp_data;
四、MySQL DQL 数据查询语言
4.1 基础查询
-- 查询所有列
SELECT * FROM employees;
-- 查询指定列
SELECT first_name, last_name, salary FROM employees;
-- 使用别名
SELECT
first_name AS '名',
last_name AS '姓',
salary AS '月薪',
salary * 12 AS '年薪'
FROM employees;
-- 去重查询
SELECT DISTINCT department_id FROM employees;
-- 条件查询
SELECT * FROM employees
WHERE salary > 5000 AND department_id = 1;
-- NULL值处理
SELECT * FROM employees
WHERE email IS NULL;
SELECT * FROM employees
WHERE email IS NOT NULL;
-- 模糊查询
SELECT * FROM employees
WHERE first_name LIKE '张%'; -- 以张开头的
SELECT * FROM employees
WHERE email LIKE '%@example.com'; -- 以@example.com结尾的
SELECT * FROM employees
WHERE last_name LIKE '_三'; -- 第二个字为三的
4.2 排序和限制
-- 单字段排序
SELECT * FROM employees
ORDER BY salary DESC;
-- 多字段排序
SELECT * FROM employees
ORDER BY department_id ASC, salary DESC;
-- 限制返回数量
SELECT * FROM employees
ORDER BY hire_date DESC
LIMIT 10;
-- 分页查询
SELECT * FROM employees
ORDER BY id
LIMIT 10 OFFSET 20; -- 第3页,每页10条
-- 简化分页写法
SELECT * FROM employees
ORDER BY id
LIMIT 20, 10; -- 从第20条开始,取10条
4.3 聚合函数
-- 基本聚合
SELECT
COUNT(*) AS '员工总数',
COUNT(DISTINCT department_id) AS '部门数量',
AVG(salary) AS '平均工资',
MAX(salary) AS '最高工资',
MIN(salary) AS '最低工资',
SUM(salary) AS '工资总额'
FROM employees;
-- 分组聚合
SELECT
department_id,
COUNT(*) AS '人数',
AVG(salary) AS '平均工资',
MAX(salary) AS '最高工资'
FROM employees
GROUP BY department_id;
-- 分组后筛选
SELECT
department_id,
COUNT(*) AS '人数'
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 5;
-- 分组后排序
SELECT
department_id,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
ORDER BY avg_salary DESC;
4.4 多表连接查询
-- INNER JOIN(内连接)
SELECT
e.first_name,
e.last_name,
e.salary,
d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
-- LEFT JOIN(左连接)
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
-- RIGHT JOIN(右连接)
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;
-- FULL OUTER JOIN(MySQL不支持,用UNION模拟)
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
UNION
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;
-- 多表连接
SELECT
e.first_name,
e.last_name,
d.department_name,
l.location_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN locations l ON d.location_id = l.location_id;
4.5 子查询
-- 标量子查询(返回单个值)
SELECT
first_name,
last_name,
salary,
(SELECT AVG(salary) FROM employees) AS avg_salary
FROM employees;
-- 列子查询(返回一列)
SELECT * FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE location = '上海'
);
-- 行子查询(返回一行)
SELECT * FROM employees
WHERE (department_id, salary) = (
SELECT department_id, MAX(salary)
FROM employees
GROUP BY department_id
ORDER BY MAX(salary) DESC
LIMIT 1
);
-- 表子查询(返回多行多列)
SELECT * FROM (
SELECT
department_id,
COUNT(*) as emp_count,
AVG(salary) as avg_salary
FROM employees
GROUP BY department_id
) AS dept_stats
WHERE emp_count > 10;
-- EXISTS子查询
SELECT * FROM departments d
WHERE EXISTS (
SELECT 1
FROM employees e
WHERE e.department_id = d.department_id
AND e.salary > 10000
);
-- NOT EXISTS子查询
SELECT * FROM departments d
WHERE NOT EXISTS (
SELECT 1
FROM employees e
WHERE e.department_id = d.department_id
);
4.6 组合查询
-- UNION(去重合并)
SELECT first_name, last_name FROM employees WHERE department_id = 1
UNION
SELECT first_name, last_name FROM employees WHERE salary > 8000;
-- UNION ALL(不去重合并)
SELECT first_name, last_name FROM employees WHERE department_id = 1
UNION ALL
SELECT first_name, last_name FROM employees WHERE salary > 8000;
-- INTERSECT(交集,MySQL 8.0.31+)
SELECT first_name, last_name FROM employees WHERE department_id = 1
INTERSECT
SELECT first_name, last_name FROM employees WHERE salary > 8000;
-- EXCEPT(差集,MySQL 8.0.31+)
SELECT first_name, last_name FROM employees WHERE department_id = 1
EXCEPT
SELECT first_name, last_name FROM employees WHERE salary > 8000;
4.7 窗口函数
-- 排名函数
SELECT
first_name,
last_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_num,
NTILE(4) OVER (ORDER BY salary DESC) AS quartile
FROM employees;
-- 聚合窗口函数
SELECT
first_name,
last_name,
department_id,
salary,
AVG(salary) OVER (PARTITION BY department_id) AS dept_avg_salary,
SUM(salary) OVER (PARTITION BY department_id) AS dept_total_salary,
COUNT(*) OVER (PARTITION BY department_id) AS dept_emp_count
FROM employees;
-- 前后值函数
SELECT
first_name,
last_name,
hire_date,
salary,
LAG(salary) OVER (ORDER BY hire_date) AS prev_salary,
LEAD(salary) OVER (ORDER BY hire_date) AS next_salary,
FIRST_VALUE(salary) OVER (PARTITION BY department_id ORDER BY salary) AS lowest_salary,
LAST_VALUE(salary) OVER (PARTITION BY department_id ORDER BY salary) AS highest_salary
FROM employees;
-- 累积计算
SELECT
hire_date,
salary,
SUM(salary) OVER (ORDER BY hire_date) AS running_total,
AVG(salary) OVER (ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM employees;
五、MySQL 索引管理
5.1 创建索引
-- 创建普通索引
CREATE INDEX idx_last_name ON employees(last_name);
-- 创建唯一索引
CREATE UNIQUE INDEX uk_email ON employees(email);
-- 创建复合索引
CREATE INDEX idx_name_department ON employees(last_name, first_name, department_id);
-- 创建前缀索引
CREATE INDEX idx_email_prefix ON employees(email(20));
-- 创建全文索引(InnoDB支持)
CREATE FULLTEXT INDEX ft_content ON articles(title, content);
-- 创建空间索引
CREATE SPATIAL INDEX sp_location ON places(location);
5.2 查看和删除索引
-- 查看表索引
SHOW INDEX FROM employees;
-- 查看索引信息
SELECT
TABLE_NAME,
INDEX_NAME,
COLUMN_NAME,
SEQ_IN_INDEX,
INDEX_TYPE,
CARDINALITY
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'company'
AND TABLE_NAME = 'employees';
-- 删除索引
DROP INDEX idx_last_name ON employees;
-- 优化表(重建索引)
OPTIMIZE TABLE employees;
-- 分析表(更新统计信息)
ANALYZE TABLE employees;
-- 检查表状态
CHECK TABLE employees;
六、MySQL 事务管理
6.1 事务控制
-- 开始事务
START TRANSACTION;
-- 或者
BEGIN;
-- 或者
BEGIN WORK;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
-- 设置保存点
SAVEPOINT point1;
-- 回滚到保存点
ROLLBACK TO point1;
-- 释放保存点
RELEASE SAVEPOINT point1;
-- 完整事务示例
START TRANSACTION;
INSERT INTO orders (customer_id, amount) VALUES (1, 100.00);
UPDATE accounts SET balance = balance - 100 WHERE customer_id = 1;
-- 可以添加检查逻辑
SELECT balance FROM accounts WHERE customer_id = 1 FOR UPDATE;
COMMIT;
6.2 事务隔离级别
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 隔离级别说明:
-- READ UNCOMMITTED: 读取未提交数据(脏读)
-- READ COMMITTED: 读取已提交数据(Oracle默认)
-- REPEATABLE READ: 可重复读(MySQL默认)
-- SERIALIZABLE: 串行化
七、MySQL 存储过程和函数
7.1 存储过程
-- 创建存储过程
DELIMITER $$
CREATE PROCEDURE GetEmployeeCount(IN dept_id INT, OUT emp_count INT)
BEGIN
SELECT COUNT(*) INTO emp_count
FROM employees
WHERE department_id = dept_id;
END$$
DELIMITER ;
-- 调用存储过程
CALL GetEmployeeCount(1, @count);
SELECT @count;
-- 带条件的存储过程
DELIMITER $$
CREATE PROCEDURE UpdateSalary(
IN emp_id INT,
IN increase_percent DECIMAL(5,2),
OUT old_salary DECIMAL(10,2),
OUT new_salary DECIMAL(10,2)
)
BEGIN
DECLARE current_salary DECIMAL(10,2);
-- 获取当前工资
SELECT salary INTO current_salary
FROM employees
WHERE id = emp_id;
SET old_salary = current_salary;
SET new_salary = current_salary * (1 + increase_percent / 100);
-- 更新工资
UPDATE employees
SET salary = new_salary
WHERE id = emp_id;
-- 记录日志
INSERT INTO salary_history (employee_id, old_salary, new_salary, change_date)
VALUES (emp_id, old_salary, new_salary, NOW());
END$$
DELIMITER ;
7.2 函数
-- 创建函数
DELIMITER $$
CREATE FUNCTION CalculateBonus(salary DECIMAL(10,2), performance_rating INT)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
DECLARE bonus DECIMAL(10,2);
IF performance_rating >= 9 THEN
SET bonus = salary * 0.3;
ELSEIF performance_rating >= 7 THEN
SET bonus = salary * 0.2;
ELSEIF performance_rating >= 5 THEN
SET bonus = salary * 0.1;
ELSE
SET bonus = 0;
END IF;
RETURN bonus;
END$$
DELIMITER ;
-- 使用函数
SELECT
first_name,
last_name,
salary,
CalculateBonus(salary, 8) AS bonus
FROM employees;
八、MySQL 触发器
-- 创建触发器
DELIMITER $$
CREATE TRIGGER before_employee_insert
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
IF NEW.email IS NULL THEN
SET NEW.email = CONCAT(LOWER(NEW.first_name), '.', LOWER(NEW.last_name), '@company.com');
END IF;
SET NEW.created_at = NOW();
SET NEW.updated_at = NOW();
END$$
DELIMITER ;
-- 更新触发器
DELIMITER $$
CREATE TRIGGER before_employee_update
BEFORE UPDATE ON employees
FOR EACH ROW
BEGIN
SET NEW.updated_at = NOW();
END$$
DELIMITER ;
-- 删除触发器
DROP TRIGGER IF EXISTS before_employee_insert;
九、MySQL 视图
-- 创建视图
CREATE VIEW employee_details AS
SELECT
e.id,
CONCAT(e.first_name, ' ', e.last_name) AS full_name,
e.email,
e.salary,
d.department_name,
e.hire_date
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.status = 'active';
-- 使用视图
SELECT * FROM employee_details WHERE department_name = '技术部';
-- 创建可更新视图
CREATE VIEW active_employees AS
SELECT id, first_name, last_name, email, department_id
FROM employees
WHERE status = 'active'
WITH CHECK OPTION;
-- 查看视图定义
SHOW CREATE VIEW employee_details;
-- 删除视图
DROP VIEW IF EXISTS employee_details;
PostgreSQL 详细教程
一、PostgreSQL 连接与基础操作
1.1 连接数据库
# 连接本地数据库
psql -U username -d database_name
# 指定主机和端口
psql -h localhost -p 5432 -U username -d database_name
# 使用连接字符串
psql "host=localhost port=5432 dbname=mydb user=myuser password=mypass"
# 连接后执行SQL文件
psql -U username -d database_name -f script.sql
# 执行单条命令
psql -U username -d database_name -c "SELECT version();"
1.2 数据库基本操作
-- 查看所有数据库
\l
-- 创建数据库
CREATE DATABASE company;
-- 指定参数创建
CREATE DATABASE company
WITH
ENCODING = 'UTF8'
LC_COLLATE = 'zh_CN.UTF-8'
LC_CTYPE = 'zh_CN.UTF-8'
TEMPLATE = template0;
-- 连接数据库
\c company
-- 查看数据库信息
SELECT datname, encoding, datcollate, datctype
FROM pg_database;
-- 删除数据库
DROP DATABASE company;
-- 安全删除
DROP DATABASE IF EXISTS company;
-- 重命名数据库
ALTER DATABASE old_name RENAME TO new_name;
二、PostgreSQL DDL 数据定义语言
2.1 创建表
-- 基本创建表
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
hire_date DATE DEFAULT CURRENT_DATE,
salary NUMERIC(10,2),
department_id INTEGER,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 带注释的表
COMMENT ON TABLE employees IS '员工信息表';
COMMENT ON COLUMN employees.id IS '员工ID';
COMMENT ON COLUMN employees.email IS '员工邮箱';
-- 继承表(PostgreSQL特有)
CREATE TABLE managers (
bonus NUMERIC(10,2),
team_size INTEGER
) INHERITS (employees);
-- 创建分区表
CREATE TABLE sales (
sale_id SERIAL,
sale_date DATE NOT NULL,
amount NUMERIC(10,2),
region VARCHAR(50)
) PARTITION BY RANGE (sale_date);
-- 创建分区
CREATE TABLE sales_2023_q1 PARTITION OF sales
FOR VALUES FROM ('2023-01-01') TO ('2023-04-01');
CREATE TABLE sales_2023_q2 PARTITION OF sales
FOR VALUES FROM ('2023-04-01') TO ('2023-07-01');
2.2 修改表结构
-- 添加列
ALTER TABLE employees ADD COLUMN phone VARCHAR(20);
-- 添加带默认值的列
ALTER TABLE employees
ADD COLUMN status VARCHAR(20) DEFAULT 'active';
-- 修改列类型
ALTER TABLE employees
ALTER COLUMN salary TYPE NUMERIC(12,2);
-- 设置列默认值
ALTER TABLE employees
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
-- 删除列默认值
ALTER TABLE employees
ALTER COLUMN updated_at DROP DEFAULT;
-- 重命名列
ALTER TABLE employees
RENAME COLUMN phone TO mobile;
-- 添加约束
ALTER TABLE employees
ADD CONSTRAINT check_salary CHECK (salary >= 0);
-- 删除约束
ALTER TABLE employees
DROP CONSTRAINT check_salary;
-- 重命名表
ALTER TABLE employees RENAME TO staff;
三、PostgreSQL DML 数据操作语言
3.1 插入数据
-- 插入单条数据
INSERT INTO employees (first_name, last_name, email, salary)
VALUES ('张', '三', 'zhangsan@example.com', 5000.00);
-- 插入多条数据
INSERT INTO employees (first_name, last_name, email, salary) VALUES
('李', '四', 'lisi@example.com', 6000.00),
('王', '五', 'wangwu@example.com', 7000.00);
-- 插入并返回ID
INSERT INTO employees (first_name, last_name, email, salary)
VALUES ('赵', '六', 'zhaoliu@example.com', 5500.00)
RETURNING id;
-- 从查询结果插入
INSERT INTO employee_backup
SELECT * FROM employees WHERE hire_date < '2020-01-01'
RETURNING *;
-- 冲突处理(不存在则插入,存在则更新)
INSERT INTO employees (id, first_name, last_name, email)
VALUES (1, '张', '三', 'zhangsan@example.com')
ON CONFLICT (id)
DO UPDATE SET
first_name = EXCLUDED.first_name,
last_name = EXCLUDED.last_name,
email = EXCLUDED.email,
updated_at = CURRENT_TIMESTAMP;
-- 冲突处理(不存在则插入,存在则忽略)
INSERT INTO employees (id, first_name, last_name, email)
VALUES (1, '张', '三', 'zhangsan@example.com')
ON CONFLICT (id) DO NOTHING;
3.2 更新数据
-- 更新数据
UPDATE employees
SET salary = 5500.00, updated_at = CURRENT_TIMESTAMP
WHERE id = 1
RETURNING *;
-- 使用FROM子句更新
UPDATE employees e
SET salary = d.avg_salary
FROM (
SELECT department_id, AVG(salary) as avg_salary
FROM employees
GROUP BY department_id
) d
WHERE e.department_id = d.department_id;
-- 使用JOIN更新
UPDATE employees e
SET salary = e.salary * 1.05
FROM departments d
WHERE e.department_id = d.department_id
AND d.department_name = '技术部';
-- 条件更新
UPDATE employees
SET salary = CASE
WHEN performance_rating >= 9 THEN salary * 1.20
WHEN performance_rating >= 7 THEN salary * 1.10
ELSE salary * 1.05
END;
3.3 删除数据
-- 删除数据
DELETE FROM employees WHERE id = 1
RETURNING *;
-- 使用USING子句删除
DELETE FROM employees e
USING departments d
WHERE e.department_id = d.department_id
AND d.location = '北京';
-- 清空表
TRUNCATE TABLE employees;
-- 清空表并重置序列
TRUNCATE TABLE employees RESTART IDENTITY;
-- 只清空数据,保留表结构
TRUNCATE TABLE employees CONTINUE IDENTITY;
四、PostgreSQL DQL 数据查询语言
4.1 CTE 公用表表达式
-- 基本CTE
WITH department_stats AS (
SELECT
department_id,
COUNT(*) as emp_count,
AVG(salary) as avg_salary
FROM employees
GROUP BY department_id
)
SELECT * FROM department_stats
WHERE emp_count > 10;
-- 递归CTE(查询树形结构)
WITH RECURSIVE category_tree AS (
-- 初始查询(根节点)
SELECT id, name, parent_id, 1 as level
FROM categories
WHERE parent_id IS NULL
UNION ALL
-- 递归查询(子节点)
SELECT c.id, c.name, c.parent_id, ct.level + 1
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree
ORDER BY level, id;
4.2 窗口函数
-- PostgreSQL窗口函数更强大
SELECT
first_name,
last_name,
department_id,
salary,
-- 排名
ROW_NUMBER() OVER w AS row_num,
RANK() OVER w AS rank_num,
DENSE_RANK() OVER w AS dense_rank,
-- 聚合
AVG(salary) OVER w AS dept_avg,
SUM(salary) OVER w AS dept_total,
-- 前后值
LAG(salary) OVER w AS prev_salary,
LEAD(salary) OVER w AS next_salary,
-- 百分位
PERCENT_RANK() OVER w AS percent_rank,
CUME_DIST() OVER w AS cumulative_dist
FROM employees
WINDOW w AS (PARTITION BY department_id ORDER BY salary DESC)
ORDER BY department_id, salary DESC;
-- 窗口帧子句
SELECT
hire_date,
salary,
AVG(salary) OVER (
ORDER BY hire_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_avg_3,
AVG(salary) OVER (
ORDER BY hire_date
RANGE BETWEEN INTERVAL '30 days' PRECEDING AND CURRENT ROW
) AS monthly_avg
FROM employees;
4.3 JSON 操作
-- 创建JSON字段
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
attributes JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入JSON数据
INSERT INTO products (name, attributes) VALUES
('笔记本电脑', '{"brand": "Dell", "ram": "16GB", "storage": "512GB SSD", "color": "silver"}'),
('智能手机', '{"brand": "Apple", "model": "iPhone 14", "color": "black", "storage": "256GB"}');
-- 查询JSON字段
SELECT
name,
attributes->>'brand' as brand,
attributes->>'color' as color,
attributes->'storage' as storage
FROM products;
-- JSON条件查询
SELECT * FROM products
WHERE attributes->>'brand' = 'Apple';
SELECT * FROM products
WHERE attributes @> '{"color": "black"}';
-- 更新JSON字段
UPDATE products
SET attributes = attributes || '{"warranty": "2 years"}'::jsonb
WHERE id = 1;
-- JSON函数
SELECT
jsonb_pretty(attributes),
jsonb_array_length(attributes->'features'),
jsonb_object_keys(attributes)
FROM products;
-- 创建JSON索引
CREATE INDEX idx_attributes ON products USING GIN (attributes);
五、PostgreSQL 索引管理
5.1 创建索引
-- B-tree索引(默认)
CREATE INDEX idx_last_name ON employees(last_name);
-- 复合索引
CREATE INDEX idx_name_dept ON employees(last_name, first_name, department_id);
-- 唯一索引
CREATE UNIQUE INDEX uk_email ON employees(email);
-- 部分索引(条件索引)
CREATE INDEX idx_active_employees ON employees(email)
WHERE status = 'active';
-- 表达式索引
CREATE INDEX idx_lower_email ON employees(LOWER(email));
CREATE INDEX idx_year ON employees(EXTRACT(YEAR FROM hire_date));
-- 覆盖索引(INCLUDE)
CREATE INDEX idx_employee_covering ON employees(department_id, hire_date)
INCLUDE (first_name, last_name, salary);
-- GIN索引(用于数组、JSON、全文搜索)
CREATE INDEX idx_attributes_gin ON products USING GIN (attributes);
-- GiST索引(用于地理空间、范围)
CREATE INDEX idx_location_gist ON places USING GIST (location);
-- BRIN索引(用于大型表的时间范围)
CREATE INDEX idx_sales_date_brin ON sales USING BRIN (sale_date);
-- 并发创建索引(不锁表)
CREATE INDEX CONCURRENTLY idx_employees_department ON employees(department_id);
六、PostgreSQL 事务管理
6.1 事务控制
-- 开始事务
BEGIN;
-- 或者
START TRANSACTION;
-- 设置事务特性
BEGIN TRANSACTION
ISOLATION LEVEL SERIALIZABLE
READ WRITE
DEFERRABLE;
-- 保存点
SAVEPOINT my_savepoint;
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 释放保存点
RELEASE SAVEPOINT my_savepoint;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
-- 完整示例
BEGIN;
INSERT INTO orders (customer_id, amount) VALUES (1, 100.00);
UPDATE accounts SET balance = balance - 100 WHERE customer_id = 1;
-- 检查约束
SAVEPOINT before_check;
-- 可以在这里添加其他操作
COMMIT;
6.2 锁机制
-- 行级锁
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 非阻塞锁
SELECT * FROM accounts WHERE id = 1 FOR UPDATE NOWAIT;
-- 跳过锁定的行
SELECT * FROM accounts WHERE id = 1 FOR UPDATE SKIP LOCKED;
-- 共享锁
SELECT * FROM accounts WHERE id = 1 FOR SHARE;
-- 表级锁
LOCK TABLE accounts IN ACCESS EXCLUSIVE MODE;
-- 查看锁信息
SELECT
locktype,
relation::regclass,
mode,
granted
FROM pg_locks
WHERE relation = 'accounts'::regclass;
七、PostgreSQL 函数和存储过程
7.1 函数
-- 创建函数
CREATE OR REPLACE FUNCTION get_employee_count(dept_id INTEGER)
RETURNS INTEGER AS $$
DECLARE
emp_count INTEGER;
BEGIN
SELECT COUNT(*) INTO emp_count
FROM employees
WHERE department_id = dept_id;
RETURN emp_count;
END;
$$ LANGUAGE plpgsql;
-- 调用函数
SELECT get_employee_count(1);
-- 返回表的函数
CREATE OR REPLACE FUNCTION get_employees_by_dept(dept_id INTEGER)
RETURNS TABLE (
emp_id INTEGER,
full_name VARCHAR(101),
salary NUMERIC(10,2)
) AS $$
BEGIN
RETURN QUERY
SELECT
e.id,
e.first_name || ' ' || e.last_name,
e.salary
FROM employees e
WHERE e.department_id = dept_id;
END;
$$ LANGUAGE plpgsql;
-- 使用表函数
SELECT * FROM get_employees_by_dept(1);
7.2 存储过程
-- 创建存储过程
CREATE OR REPLACE PROCEDURE update_salary(
emp_id INTEGER,
increase_percent NUMERIC
)
LANGUAGE plpgsql
AS $$
DECLARE
old_salary NUMERIC(10,2);
new_salary NUMERIC(10,2);
BEGIN
-- 获取当前工资
SELECT salary INTO old_salary
FROM employees
WHERE id = emp_id;
-- 计算新工资
new_salary := old_salary * (1 + increase_percent / 100);
-- 更新工资
UPDATE employees
SET salary = new_salary
WHERE id = emp_id;
-- 记录日志
INSERT INTO salary_history
(employee_id, old_salary, new_salary, change_date)
VALUES (emp_id, old_salary, new_salary, NOW());
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
$$;
-- 调用存储过程
CALL update_salary(1, 10.0);
Redis 详细教程
一、Redis 基础操作
1.1 连接和配置
# 连接Redis
redis-cli
# 指定主机和端口
redis-cli -h localhost -p 6379
# 密码认证
redis-cli -a password
# 连接指定数据库(0-15)
redis-cli -n 1
# 执行命令
redis-cli set key value
redis-cli get key
# 批量执行命令
echo -e "set key1 value1\nget key1" | redis-cli
# 查看配置
redis-cli config get *
redis-cli config get maxmemory
1.2 键操作
-- 设置键值
SET user:1:name "张三"
SET user:1:age 25
SET user:1:email "zhangsan@example.com"
-- 获取键值
GET user:1:name
-- 检查键是否存在
EXISTS user:1:name
-- 删除键
DEL user:1:email
-- 设置过期时间
SET session:abc123 "userdata" EX 3600 -- 3600秒后过期
EXPIRE user:1:session 1800 -- 1800秒后过期
-- 查看剩余时间
TTL user:1:session
-- 移除过期时间
PERSIST user:1:session
-- 重命名键
RENAME user:1:name user:1:fullname
-- 随机获取一个键
RANDOMKEY
-- 扫描键(分批处理大量键)
SCAN 0 MATCH "user:*" COUNT 100
二、Redis 数据结构操作
2.1 字符串(String)
-- 基本操作
SET counter 100
INCR counter -- 增加到101
DECR counter -- 减少到100
INCRBY counter 50 -- 增加50
DECRBY counter 20 -- 减少20
-- 批量操作
MSET key1 "value1" key2 "value2" key3 "value3"
MGET key1 key2 key3
-- 追加操作
APPEND greeting "Hello"
APPEND greeting " World" -- 变成"Hello World"
-- 获取部分字符串
SET message "Hello Redis"
GETRANGE message 0 4 -- "Hello"
GETRANGE message 6 10 -- "Redis"
-- 设置并获取旧值
GETSET counter 200 -- 返回旧值,设置新值
-- 位操作
SETBIT mykey 7 1 -- 设置第7位为1
GETBIT mykey 7 -- 获取第7位的值
BITCOUNT mykey -- 统计1的位数
2.2 哈希(Hash)
-- 设置哈希字段
HSET user:1000 name "张三"
HSET user:1000 age 30
HSET user:1000 email "zhangsan@example.com"
-- 批量设置
HMSET user:1001 name "李四" age 25 email "lisi@example.com"
-- 获取字段
HGET user:1000 name
HMGET user:1000 name age email
-- 获取所有字段和值
HGETALL user:1000
-- 获取所有字段名
HKEYS user:1000
-- 获取所有值
HVALS user:1000
-- 检查字段是否存在
HEXISTS user:1000 email
-- 删除字段
HDEL user:1000 email
-- 字段自增
HINCRBY user:1000 age 1
-- 字段长度
HLEN user:1000
-- 设置字段(不存在时才设置)
HSETNX user:1000 address "北京"
2.3 列表(List)
-- 左侧插入
LPUSH tasks "task1"
LPUSH tasks "task2" "task3" -- 可以一次插入多个
-- 右侧插入
RPUSH tasks "task4"
RPUSH tasks "task5" "task6"
-- 获取列表长度
LLEN tasks
-- 获取元素
LINDEX tasks 0 -- 获取第一个元素
LINDEX tasks -1 -- 获取最后一个元素
-- 范围获取
LRANGE tasks 0 2 -- 获取前3个元素
LRANGE tasks 0 -1 -- 获取所有元素
-- 弹出元素
LPOP tasks -- 左侧弹出
RPOP tasks -- 右侧弹出
-- 阻塞弹出(队列模式)
BLPOP queue 30 -- 阻塞30秒等待元素
BRPOP queue 30
-- 插入元素
LINSERT tasks BEFORE "task2" "newtask" -- 在task2前插入
LINSERT tasks AFTER "task2" "newtask" -- 在task2后插入
-- 移除元素
LREM tasks 2 "task1" -- 从列表移除2个task1
LREM tasks 0 "task1" -- 移除所有task1
-- 修剪列表
LTRIM tasks 0 4 -- 只保留前5个元素
-- 设置元素
LSET tasks 0 "newtask1" -- 设置索引0的元素
2.4 集合(Set)
-- 添加元素
SADD tags "redis" "database" "nosql"
SADD tags "redis" -- 重复元素会被忽略
-- 获取所有元素
SMEMBERS tags
-- 检查元素是否存在
SISMEMBER tags "redis"
-- 移除元素
SREM tags "nosql"
-- 随机获取元素
SPOP tags -- 移除并返回一个随机元素
SRANDMEMBER tags -- 返回但不移除随机元素
SRANDMEMBER tags 2 -- 返回2个随机元素
-- 集合运算
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"
-- 交集
SINTER set1 set2 -- "b" "c"
-- 并集
SUNION set1 set2 -- "a" "b" "c" "d"
-- 差集
SDIFF set1 set2 -- "a"(在set1但不在set2)
SDIFF set2 set1 -- "d"(在set2但不在set1)
-- 集合大小
SCARD tags
-- 移动元素
SMOVE set1 set2 "a" -- 将a从set1移动到set2
2.5 有序集合(Sorted Set)
-- 添加元素(带分数)
ZADD leaderboard 100 "player1"
ZADD leaderboard 200 "player2" 150 "player3"
-- 获取分数
ZSCORE leaderboard "player1"
-- 增加分数
ZINCRBY leaderboard 50 "player1" -- player1分数变为150
-- 按排名获取
ZRANGE leaderboard 0 -1 -- 获取所有元素(按分数升序)
ZRANGE leaderboard 0 -1 WITHSCORES -- 带分数
ZREVRANGE leaderboard 0 2 WITHSCORES -- 降序前3名
-- 按分数范围获取
ZRANGEBYSCORE leaderboard 100 200 -- 分数在100-200之间的元素
ZRANGEBYSCORE leaderboard 100 (200 -- 分数>=100且<200
ZRANGEBYSCORE leaderboard -inf +inf -- 所有元素
-- 获取排名
ZRANK leaderboard "player1" -- 升序排名(从0开始)
ZREVRANK leaderboard "player1" -- 降序排名
-- 获取元素数量
ZCARD leaderboard
ZCOUNT leaderboard 100 200 -- 分数在100-200之间的元素数量
-- 移除元素
ZREM leaderboard "player1"
-- 按排名移除
ZREMRANGEBYRANK leaderboard 0 2 -- 移除排名0-2的元素
-- 按分数移除
ZREMRANGEBYSCORE leaderboard 0 100 -- 移除分数0-100的元素
-- 集合运算
ZADD zset1 1 "a" 2 "b" 3 "c"
ZADD zset2 2 "b" 3 "c" 4 "d"
-- 交集
ZINTERSTORE result 2 zset1 zset2 WEIGHTS 1 1 AGGREGATE SUM
-- 并集
ZUNIONSTORE result 2 zset1 zset2 WEIGHTS 1 1 AGGREGATE SUM
三、Redis 高级功能
3.1 发布订阅
-- 订阅频道
SUBSCRIBE news notifications
-- 在另一个客户端发布消息
PUBLISH news "Breaking news!"
PUBLISH notifications "New message received"
-- 模式订阅
PSUBSCRIBE user.* -- 订阅所有user.开头的频道
-- 取消订阅
UNSUBSCRIBE news
PUNSUBSCRIBE user.*
-- 查看订阅
PUBSUB CHANNELS -- 查看活跃频道
PUBSUB NUMSUB news notifications -- 查看订阅者数量
3.2 事务
-- 开启事务
MULTI
-- 添加命令到队列
SET balance:1 1000
SET balance:2 2000
INCRBY balance:1 500
DECRBY balance:2 500
-- 执行事务
EXEC
-- 取消事务
MULTI
SET key1 "value1"
DISCARD -- 取消事务
-- 监视键(乐观锁)
WATCH balance:1 balance:2
MULTI
INCRBY balance:1 100
INCRBY balance:2 -100
EXEC -- 如果在EXEC前有其他人修改了balance:1或balance:2,事务会失败
UNWATCH -- 取消监视
3.3 Lua脚本
-- 执行简单脚本
EVAL "return {KEYS[1],ARGV[1]}" 1 key1 "hello"
-- 缓存脚本
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
-- 返回: "abcdef123456..." (脚本SHA1)
-- 使用缓存的脚本
EVALSHA abcdef123456... 1 mykey
-- 检查脚本是否存在
SCRIPT EXISTS abcdef123456...
-- 清空脚本缓存
SCRIPT FLUSH
-- 复杂示例:原子性地检查并设置
EVAL "
local current = redis.call('GET', KEYS[1])
if current == ARGV[1] then
redis.call('SET', KEYS[1], ARGV[2])
return 1
else
return 0
end
" 1 username:lock session_id new_session_id
3.4 管道(Pipeline)
# 使用管道批量执行命令
(echo -en "PING\r\nSET key1 value1\r\nGET key1\r\nINCR counter\r\n"; sleep 1) | nc localhost 6379
# 或者使用redis-cli管道模式
echo -e "SET key1 value1\nGET key1" | redis-cli --pipe
四、Redis 持久化和备份
4.1 持久化配置
-- 查看持久化配置
CONFIG GET save
CONFIG GET appendonly
-- RDB持久化(快照)
-- 手动保存
SAVE -- 阻塞保存
BGSAVE -- 后台保存
-- 查看RDB信息
INFO persistence
-- AOF持久化(追加日志)
-- 开启AOF
CONFIG SET appendonly yes
-- 重写AOF文件
BGREWRITEAOF
-- 同步策略
CONFIG SET appendfsync always -- 每次写入都同步
CONFIG SET appendfsync everysec -- 每秒同步(默认)
CONFIG SET appendfsync no -- 由操作系统决定
-- 混合持久化(RDB+AOF)
CONFIG SET aof-use-rdb-preamble yes
4.2 备份恢复
# 备份RDB文件
cp /var/lib/redis/dump.rdb /backup/redis-backup-$(date +%Y%m%d).rdb
# 备份AOF文件
cp /var/lib/redis/appendonly.aof /backup/redis-aof-$(date +%Y%m%d).aof
# 从RDB恢复
# 1. 停止Redis
redis-cli shutdown
# 2. 恢复备份文件
cp /backup/dump.rdb /var/lib/redis/
# 3. 启动Redis
redis-server /etc/redis/redis.conf
# 从AOF恢复
# 1. 复制AOF文件
cp /backup/appendonly.aof /var/lib/redis/
# 2. 启动Redis(会自动加载AOF)
MongoDB 详细教程
一、MongoDB 基础操作
1.1 连接数据库
# 连接本地MongoDB
mongosh
# 连接远程MongoDB
mongosh "mongodb://username:password@hostname:27017/database"
# 连接复制集
mongosh "mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=rs0"
# 连接MongoDB Atlas
mongosh "mongodb+srv://cluster0.mongodb.net/database" --apiVersion 1 --username username
# 指定认证数据库
mongosh -u username -p password --authenticationDatabase admin
# 执行JavaScript文件
mongosh --quiet script.js
1.2 数据库操作
// 查看所有数据库
show dbs
// 查看当前数据库
db
// 创建/切换数据库
use company
// 查看数据库状态
db.stats()
// 查看数据库信息
db.runCommand({dbStats: 1})
// 删除数据库(需要先切换到该数据库)
use old_database
db.dropDatabase()
二、MongoDB 集合操作
2.1 创建和查看集合
// 创建集合
db.createCollection("employees")
db.createCollection("logs", {
capped: true,
size: 1000000,
max: 1000
})
// 查看所有集合
show collections
// 查看集合信息
db.employees.stats()
db.getCollectionInfos()
// 重命名集合
db.employees.renameCollection("staff")
// 删除集合
db.employees.drop()
2.2 索引管理
// 创建索引
db.employees.createIndex({ email: 1 }) // 升序索引
db.employees.createIndex({ email: 1 }, { unique: true }) // 唯一索引
db.employees.createIndex({ last_name: 1, first_name: 1 }) // 复合索引
// 创建文本索引
db.articles.createIndex(
{ title: "text", content: "text" },
{ weights: { title: 10, content: 5 } }
)
// 创建地理空间索引
db.places.createIndex({ location: "2dsphere" })
// 创建TTL索引(自动过期)
db.sessions.createIndex(
{ created_at: 1 },
{ expireAfterSeconds: 3600 } // 1小时后自动删除
)
// 查看索引
db.employees.getIndexes()
// 删除索引
db.employees.dropIndex("email_1")
db.employees.dropIndexes() // 删除所有索引(除了_id)
三、MongoDB CRUD 操作
3.1 插入文档
// 插入单个文档
db.employees.insertOne({
first_name: "张",
last_name: "三",
email: "zhangsan@example.com",
age: 30,
department: "技术部",
skills: ["Java", "Python", "MongoDB"],
address: {
city: "北京",
street: "中关村大街"
},
hire_date: new Date("2020-01-15"),
salary: 8000
})
// 插入多个文档
db.employees.insertMany([
{
first_name: "李",
last_name: "四",
email: "lisi@example.com",
age: 25,
department: "市场部",
salary: 6000
},
{
first_name: "王",
last_name: "五",
email: "wangwu@example.com",
age: 28,
department: "技术部",
salary: 7500
}
])
// 插入并返回文档
const result = db.employees.insertOne({
first_name: "赵",
last_name: "六",
email: "zhaoliu@example.com",
salary: 9000
})
print("插入的ID:", result.insertedId)
3.2 查询文档
// 查询所有文档
db.employees.find()
// 格式化输出
db.employees.find().pretty()
// 条件查询
db.employees.find({ department: "技术部" })
db.employees.find({ age: { $gt: 25 } }) // 年龄大于25
db.employees.find({ age: { $gte: 25, $lte: 35 } }) // 年龄25-35
db.employees.find({ department: { $in: ["技术部", "市场部"] } })
db.employees.find({ department: { $nin: ["人事部"] } })
// 复合条件查询
db.employees.find({
department: "技术部",
salary: { $gt: 7000 }
})
// OR查询
db.employees.find({
$or: [
{ department: "技术部" },
{ salary: { $gt: 8000 } }
]
})
// 嵌套文档查询
db.employees.find({ "address.city": "北京" })
// 数组查询
db.employees.find({ skills: "Java" }) // 包含Java技能
db.employees.find({ skills: { $all: ["Java", "Python"] } }) // 同时包含
db.employees.find({ skills: { $size: 3 } }) // 有3个技能
// 正则表达式查询
db.employees.find({ email: /@example\.com$/ })
db.employees.find({ last_name: /^张/ }) // 姓氏以张开头的
// 投影(选择字段)
db.employees.find(
{ department: "技术部" },
{ first_name: 1, last_name: 1, email: 1, _id: 0 }
)
// 排除字段
db.employees.find(
{},
{ skills: 0, address: 0 }
)
// 分页查询
db.employees.find().skip(20).limit(10) // 第3页,每页10条
// 排序
db.employees.find().sort({ salary: -1 }) // 按工资降序
db.employees.find().sort({ department: 1, salary: -1 }) // 部门升序,工资降序
// 计数
db.employees.countDocuments({ department: "技术部" })
db.employees.estimatedDocumentCount() // 估算总数(更快)
// 去重
db.employees.distinct("department")
3.3 更新文档
// 更新单个文档
db.employees.updateOne(
{ email: "zhangsan@example.com" },
{ $set: { salary: 8500 } }
)
// 更新多个文档
db.employees.updateMany(
{ department: "技术部" },
{ $set: { salary: 9000 } }
)
// 替换整个文档
db.employees.replaceOne(
{ email: "zhangsan@example.com" },
{
first_name: "张",
last_name: "三",
email: "zhangsan@example.com",
salary: 9000,
updated_at: new Date()
}
)
// 更新操作符
db.employees.updateOne(
{ email: "zhangsan@example.com" },
{
$set: { salary: 9000 }, // 设置字段
$inc: { age: 1 }, // 增加字段值
$push: { skills: "Go" }, // 添加到数组
$pull: { skills: "PHP" }, // 从数组移除
$addToSet: { skills: "Docker" }, // 添加到数组(不重复)
$rename: { "address.city": "city" }, // 重命名字段
$unset: { temp_field: "" }, // 删除字段
$currentDate: { updated_at: true } // 设置当前时间
}
)
// 数组更新操作符
db.employees.updateOne(
{ email: "zhangsan@example.com" },
{
$push: {
skills: {
$each: ["Go", "Rust"], // 添加多个元素
$position: 0, // 插入到数组开头
$sort: 1, // 数组排序
$slice: 5 // 只保留前5个元素
}
}
}
)
// 查找并更新(原子操作)
const result = db.employees.findOneAndUpdate(
{ email: "zhangsan@example.com" },
{ $inc: { salary: 500 } },
{
returnDocument: "after", // 返回更新后的文档
upsert: true // 如果不存在则插入
}
)
3.4 删除文档
// 删除单个文档
db.employees.deleteOne({ email: "zhangsan@example.com" })
// 删除多个文档
db.employees.deleteMany({ department: "人事部" })
// 查找并删除
const result = db.employees.findOneAndDelete(
{ email: "zhangsan@example.com" }
)
// 删除所有文档(清空集合)
db.employees.deleteMany({})
// 使用remove(已弃用,建议使用deleteOne/deleteMany)
db.employees.remove({ department: "人事部" })
四、MongoDB 聚合管道
4.1 基本聚合
// 简单聚合
db.employees.aggregate([
// 阶段1:筛选
{ $match: { department: "技术部" } },
// 阶段2:分组
{ $group: {
_id: "$department",
total_employees: { $sum: 1 },
avg_salary: { $avg: "$salary" },
max_salary: { $max: "$salary" },
min_salary: { $min: "$salary" },
total_salary: { $sum: "$salary" }
}},
// 阶段3:排序
{ $sort: { avg_salary: -1 } },
// 阶段4:限制
{ $limit: 10 }
])
// 多字段分组
db.employees.aggregate([
{ $group: {
_id: {
department: "$department",
city: "$address.city"
},
count: { $sum: 1 }
}}
])
// 展开数组
db.employees.aggregate([
{ $match: { skills: { $exists: true } } },
{ $unwind: "$skills" }, // 每个技能变成一个文档
{ $group: {
_id: "$skills",
count: { $sum: 1 }
}},
{ $sort: { count: -1 } }
])
4.2 高级聚合
// 多集合关联查询
db.orders.aggregate([
// 筛选订单
{ $match: { status: "completed" } },
// 关联用户表
{ $lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user_info"
}},
// 展开关联结果(一对一关系)
{ $unwind: "$user_info" },
// 关联产品表(一对多关系)
{ $lookup: {
from: "products",
localField: "product_ids",
foreignField: "_id",
as: "products"
}},
// 分组统计
{ $group: {
_id: "$user_info.city",
total_orders: { $sum: 1 },
total_amount: { $sum: "$amount" },
avg_amount: { $avg: "$amount" }
}},
// 筛选分组结果
{ $match: { total_orders: { $gt: 5 } } },
// 排序
{ $sort: { total_amount: -1 } },
// 输出到集合
{ $out: "city_sales_stats" }
])
// 条件聚合
db.employees.aggregate([
{ $project: {
name: { $concat: ["$first_name", " ", "$last_name"] },
salary: 1,
salary_grade: {
$switch: {
branches: [
{ case: { $gte: ["$salary", 10000] }, then: "A" },
{ case: { $gte: ["$salary", 7000] }, then: "B" },
{ case: { $gte: ["$salary", 5000] }, then: "C" }
],
default: "D"
}
}
}}
])
// 日期聚合
db.orders.aggregate([
{ $project: {
year: { $year: "$order_date" },
month: { $month: "$order_date" },
week: { $week: "$order_date" },
dayOfWeek: { $dayOfWeek: "$order_date" },
amount: 1
}},
{ $group: {
_id: { year: "$year", month: "$month" },
total_amount: { $sum: "$amount" },
order_count: { $sum: 1 }
}},
{ $sort: { "_id.year": 1, "_id.month": 1 } }
])
五、MongoDB 高级功能
5.1 地理空间查询
// 创建地理空间索引
db.places.createIndex({ location: "2dsphere" })
// 插入地理空间数据
db.places.insertMany([
{
name: "天安门",
category: "景点",
location: {
type: "Point",
coordinates: [116.3974, 39.9093] // [经度, 纬度]
}
},
{
name: "故宫",
category: "景点",
location: {
type: "Point",
coordinates: [116.3972, 39.9163]
}
}
])
// 附近查询(10公里内)
db.places.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [116.3974, 39.9093]
},
$maxDistance: 10000 // 10公里
}
}
})
// 多边形区域查询
db.places.find({
location: {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: [[
[116.38, 39.90],
[116.41, 39.90],
[116.41, 39.92],
[116.38, 39.92],
[116.38, 39.90]
]]
}
}
}
})
// 距离计算
db.places.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [116.3974, 39.9093] },
distanceField: "distance",
maxDistance: 5000,
spherical: true
}
},
{ $sort: { distance: 1 } }
])
5.2 文本搜索
// 创建文本索引
db.articles.createIndex(
{ title: "text", content: "text" },
{
weights: { title: 10, content: 5 },
default_language: "english"
}
)
// 文本搜索
db.articles.find(
{ $text: { $search: "database mongodb" } }
)
// 短语搜索
db.articles.find(
{ $text: { $search: "\"mongodb database\"" } }
)
// 排除词
db.articles.find(
{ $text: { $search: "mongodb -sql" } } // 包含mongodb但不包含sql
)
// 文本搜索排序
db.articles.find(
{ $text: { $search: "database" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })
5.3 事务管理
// MongoDB 4.0+ 支持多文档事务
const session = db.getMongo().startSession();
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
});
try {
const users = session.getDatabase("company").users;
const accounts = session.getDatabase("company").accounts;
// 在事务中执行多个操作
users.insertOne({
_id: 1001,
name: "张三",
email: "zhangsan@example.com"
});
accounts.insertOne({
user_id: 1001,
balance: 1000,
created_at: new Date()
});
// 提交事务
session.commitTransaction();
console.log("事务提交成功");
} catch (error) {
console.log("事务失败,回滚:", error);
session.abortTransaction();
} finally {
session.endSession();
}
六、MongoDB 性能优化
6.1 查询优化
// 使用投影减少返回数据
db.employees.find({}, { first_name: 1, last_name: 1 })
// 使用覆盖查询(索引包含所有查询字段)
db.employees.createIndex({ department: 1, salary: 1 })
db.employees.find(
{ department: "技术部", salary: { $gt: 7000 } },
{ _id: 0, department: 1, salary: 1 }
) // 这个查询可以从索引中获取所有数据,不需要访问文档
// 使用索引排序
db.employees.find().sort({ salary: 1 }) // 需要有salary索引
// 避免全表扫描
db.employees.find({ email: /^zhang/ }) // 可以走索引
db.employees.find({ email: /zhang/ }) // 不能走索引
// 使用hint强制使用索引
db.employees.find({ department: "技术部" }).hint({ department: 1 })
// 监控查询性能
db.employees.find({ salary: { $gt: 7000 } }).explain("executionStats")
// 设置查询超时
db.employees.find({ department: "技术部" }).maxTimeMS(1000)
6.2 批量操作
// 批量插入
const bulk = db.employees.initializeUnorderedBulkOp();
for (let i = 0; i < 1000; i++) {
bulk.insert({
first_name: "User",
last_name: i.toString(),
email: `user${i}@example.com`,
salary: Math.floor(Math.random() * 5000) + 3000
});
}
bulk.execute();
// 批量更新
const bulk = db.employees.initializeUnorderedBulkOp();
bulk.find({ department: "技术部" }).update({ $set: { salary: 9000 } });
bulk.find({ department: "市场部" }).update({ $inc: { salary: 500 } });
bulk.execute();
// 批量操作选项
db.employees.bulkWrite([
{ insertOne: { document: { name: "张三" } } },
{ updateOne: {
filter: { name: "李四" },
update: { $set: { salary: 8000 } }
}},
{ deleteOne: { filter: { name: "王五" } } }
], {
ordered: false, // 无序执行(更快)
writeConcern: { w: 1 }
})
附录:常见错误和解决方案
MySQL 常见问题
-- 1. 连接数过多
SHOW PROCESSLIST;
SHOW VARIABLES LIKE 'max_connections';
SET GLOBAL max_connections = 1000;
-- 2. 慢查询
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 2;
-- 3. 死锁处理
SHOW ENGINE INNODB STATUS;
SELECT * FROM information_schema.innodb_locks;
SELECT * FROM information_schema.innodb_lock_waits;
-- 4. 表损坏修复
CHECK TABLE table_name;
REPAIR TABLE table_name;
ALTER TABLE table_name ENGINE=InnoDB;
-- 5. 内存不足
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SET GLOBAL innodb_buffer_pool_size = 4294967296; -- 4GB
PostgreSQL 常见问题
-- 1. 连接数过多
SELECT count(*) FROM pg_stat_activity;
SELECT max_conn FROM pg_settings WHERE name = 'max_connections';
ALTER SYSTEM SET max_connections = 200;
-- 2. 死锁
SELECT * FROM pg_locks WHERE granted = false;
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle';
-- 3. 表膨胀
VACUUM FULL table_name;
REINDEX TABLE table_name;
-- 4. WAL日志过大
SELECT * FROM pg_stat_archiver;
ALTER SYSTEM SET wal_keep_segments = 64;
-- 5. 查询慢
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM table_name;
SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10;
Redis 常见问题
-- 1. 内存使用过高
INFO memory
CONFIG SET maxmemory 2gb
CONFIG SET maxmemory-policy allkeys-lru
-- 2. 慢查询
SLOWLOG GET 10
CONFIG SET slowlog-log-slower-than 10000
-- 3. AOF文件过大
BGREWRITEAOF
CONFIG SET auto-aof-rewrite-percentage 100
CONFIG SET auto-aof-rewrite-min-size 64mb
-- 4. 主从同步延迟
INFO replication
CONFIG SET repl-backlog-size 100mb
-- 5. 键过多
SCAN 0 COUNT 100
CONFIG SET databases 16
MongoDB 常见问题
// 1. 连接数过多
db.serverStatus().connections
db.adminCommand({setParameter: 1, maxConnections: 1000})
// 2. 内存使用高
db.serverStatus().mem
db.adminCommand({setParameter: 1, wiredTigerEngineRuntimeConfig: "cache_size=4G"})
// 3. 查询慢
db.setProfilingLevel(1, 100)
db.system.profile.find().sort({ts: -1}).limit(10)
// 4. 索引缺失
db.collection.getIndexes()
db.collection.createIndex({field: 1})
// 5. 分片不均
db.adminCommand({flushRouterConfig: 1})
db.adminCommand({moveChunk: "db.collection", find: {_id: ObjectId()}, to: "shard2"})