SQL 中的流程控制函数
SQL 中的流程控制函数允许你在查询中实现条件逻辑,类似于编程语言中的 if-else、switch-case 等语句。这些函数在不同数据库系统中语法可能略有不同,下面我会详细介绍主要数据库的流程控制函数。
一、通用流程控制函数
1. CASE 表达式 (所有主流数据库都支持)
CASE 表达式是最重要、最通用的流程控制结构,有两种形式:
形式一:简单 CASE 表达式
sql
-- 类似于编程语言的 switch-case
SELECT
employee_name,
department_id,
CASE department_id
WHEN 1 THEN '技术部'
WHEN 2 THEN '销售部'
WHEN 3 THEN '财务部'
ELSE '其他部门'
END AS department_name,
salary
FROM employees;
形式二:搜索 CASE 表达式
sql
-- 类似于编程语言的 if-else if-else
SELECT
employee_name,
salary,
CASE
WHEN salary >= 10000 THEN '高薪'
WHEN salary >= 6000 THEN '中薪'
WHEN salary >= 3000 THEN '低薪'
ELSE '实习薪资'
END AS salary_level,
CASE
WHEN salary > 10000 AND performance_rating = 'A' THEN '优秀员工'
WHEN salary BETWEEN 6000 AND 10000 THEN '合格员工'
ELSE '待改进'
END AS employee_status
FROM employees;
在 UPDATE 语句中使用 CASE
sql
-- 根据条件更新不同的值
UPDATE employees
SET salary = CASE
WHEN performance_rating = 'A' THEN salary * 1.2
WHEN performance_rating = 'B' THEN salary * 1.1
WHEN performance_rating = 'C' THEN salary * 1.05
ELSE salary
END,
bonus = CASE
WHEN years_of_service > 5 THEN 5000
WHEN years_of_service > 2 THEN 2000
ELSE 500
END
WHERE active = 1;
在 ORDER BY 中使用 CASE
sql
-- 自定义排序规则
SELECT
product_name,
category,
price
FROM products
ORDER BY
CASE
WHEN category = '热门' THEN 1
WHEN category = '推荐' THEN 2
WHEN category = '新品' THEN 3
ELSE 4
END,
price DESC;
二、MySQL 特有的流程控制函数
1. IF() 函数
sql
-- 语法: IF(condition, value_if_true, value_if_false)
SELECT
product_name,
price,
IF(price > 100, '高价商品', '普通商品') AS price_type,
IF(stock_quantity > 0, '有货', '缺货') AS stock_status,
IF(discount IS NULL, price, price * discount) AS final_price
FROM products;
2. IFNULL() 函数
sql
-- 语法: IFNULL(expression, replacement_value)
SELECT
customer_name,
IFNULL(phone, '未提供电话') AS contact_phone,
IFNULL(email, '未提供邮箱') AS contact_email,
-- 处理可能的NULL值计算
IFNULL(total_amount, 0) * IFNULL(discount_rate, 1) AS actual_amount
FROM customers;
3. NULLIF() 函数
sql
-- 语法: NULLIF(expr1, expr2)
-- 如果两个表达式相等返回NULL,否则返回第一个表达式
SELECT
employee_id,
NULLIF(salary, 0) AS actual_salary, -- 如果薪资为0,显示为NULL
NULLIF(department, '未分配') AS department_name
FROM employees;
4. 在存储过程中使用流程控制
sql
DELIMITER //
CREATE PROCEDURE CalculateEmployeeBonus(IN emp_id INT)
BEGIN
DECLARE emp_salary DECIMAL(10,2);
DECLARE emp_rating VARCHAR(1);
DECLARE bonus_amount DECIMAL(10,2);
-- 获取员工薪资和评级
SELECT salary, performance_rating INTO emp_salary, emp_rating
FROM employees WHERE employee_id = emp_id;
-- 使用 CASE 计算奖金
SET bonus_amount = CASE
WHEN emp_rating = 'A' THEN emp_salary * 0.2
WHEN emp_rating = 'B' THEN emp_salary * 0.1
WHEN emp_rating = 'C' THEN emp_salary * 0.05
ELSE 0
END;
-- 使用 IF 判断是否发放奖金
IF bonus_amount > 0 THEN
INSERT INTO bonus_records(employee_id, bonus_amount, bonus_date)
VALUES (emp_id, bonus_amount, CURDATE());
SELECT CONCAT('奖金已发放: ', bonus_amount) AS result;
ELSE
SELECT '该员工不符合奖金发放条件' AS result;
END IF;
END //
DELIMITER ;
三、SQL Server 特有的流程控制函数
1. IIF() 函数 (SQL Server 2012+)
sql
-- 语法: IIF(boolean_expression, true_value, false_value)
SELECT
product_name,
price,
IIF(price > 100, '高价', '普通') AS price_category,
IIF(stock_count > 0 AND active = 1, '可销售', '不可销售') AS status
FROM products;
2. CHOOSE() 函数
sql
-- 语法: CHOOSE(index, val_1, val_2, val_3, ...)
SELECT
employee_name,
department_id,
CHOOSE(department_id, '技术部', '销售部', '财务部', '人事部') AS department_name,
MONTH(hire_date) AS hire_month,
CHOOSE(MONTH(hire_date), '一月', '二月', '三月', '四月', '五月', '六月',
'七月', '八月', '九月', '十月', '十一月', '十二月') AS hire_month_name
FROM employees;
3. COALESCE() 函数 (多数据库支持)
sql
-- 返回参数列表中第一个非NULL的值
SELECT
customer_id,
COALESCE(nickname, first_name, '未知用户') AS display_name,
COALESCE(home_phone, work_phone, mobile_phone, '无联系方式') AS contact_number,
COALESCE(discount_rate, default_discount, 0) AS actual_discount
FROM customers;
四、Oracle 数据库的流程控制
1. DECODE() 函数 (Oracle 特有)
sql
-- 语法: DECODE(expression, search1, result1, search2, result2, ..., default)
SELECT
employee_name,
department_id,
DECODE(department_id,
1, '技术部',
2, '销售部',
3, '财务部',
'其他部门') AS department_name,
DECODE(UPPER(gender), 'M', '男', 'F', '女', '未知') AS gender_cn
FROM employees;
2. NVL() 和 NVL2() 函数
sql
-- NVL: 如果第一个参数为NULL,返回第二个参数
SELECT
product_name,
NVL(description, '暂无描述') AS product_description,
NVL(price, 0) AS product_price
FROM products;
-- NVL2: 如果第一个参数不为NULL返回第二个参数,否则返回第三个参数
SELECT
employee_name,
NVL2(manager_id, '有上级', '无上级') AS has_manager,
NVL2(bonus, bonus * 1.1, 1000) AS adjusted_bonus
FROM employees;
五、实际业务场景示例
场景1:用户等级分类
sql
SELECT
user_id,
username,
total_orders,
total_amount,
CASE
WHEN total_amount >= 10000 THEN 'VIP用户'
WHEN total_amount >= 5000 THEN '高级用户'
WHEN total_amount >= 1000 THEN '普通用户'
WHEN total_amount > 0 THEN '新用户'
ELSE '潜在用户'
END AS user_level,
CASE
WHEN total_orders = 0 THEN '暂无订单'
WHEN total_orders <= 5 THEN '订单较少'
WHEN total_orders <= 20 THEN '订单一般'
ELSE '订单活跃'
END AS order_activity
FROM users
ORDER BY total_amount DESC;
场景2:销售业绩评估
sql
SELECT
salesperson_id,
salesperson_name,
region,
quarterly_sales,
last_quarter_sales,
-- 业绩增长率
CASE
WHEN last_quarter_sales = 0 THEN '新员工'
WHEN quarterly_sales > last_quarter_sales * 1.2 THEN '高速增长'
WHEN quarterly_sales > last_quarter_sales THEN '稳定增长'
WHEN quarterly_sales = last_quarter_sales THEN '持平'
ELSE '需要改进'
END AS growth_status,
-- 绝对业绩评估
CASE
WHEN quarterly_sales > 100000 THEN '卓越'
WHEN quarterly_sales > 50000 THEN '优秀'
WHEN quarterly_sales > 20000 THEN '合格'
ELSE '待提升'
END AS performance_rating
FROM sales_performance;
场景3:库存预警系统
sql
SELECT
product_id,
product_name,
current_stock,
min_stock_level,
max_stock_level,
CASE
WHEN current_stock = 0 THEN '缺货'
WHEN current_stock <= min_stock_level THEN '库存不足'
WHEN current_stock >= max_stock_level THEN '库存过剩'
ELSE '库存正常'
END AS stock_status,
CASE
WHEN current_stock <= min_stock_level THEN min_stock_level - current_stock
ELSE 0
END AS need_to_order
FROM inventory
WHERE active = 1;
六、性能考虑和最佳实践
1. 避免过度嵌套
sql
-- 不推荐:过度嵌套难以维护
SELECT
CASE
WHEN condition1 THEN
CASE
WHEN sub_condition1 THEN 'A1'
ELSE 'A2'
END
ELSE
CASE
WHEN condition2 THEN 'B1'
ELSE 'B2'
END
END AS result
FROM table;
-- 推荐:简化逻辑
SELECT
CASE
WHEN condition1 AND sub_condition1 THEN 'A1'
WHEN condition1 THEN 'A2'
WHEN condition2 THEN 'B1'
ELSE 'B2'
END AS result
FROM table;
2. 使用 COALESCE 处理多个 NULL 值
sql
-- 优于嵌套的 CASE 或 IFNULL
SELECT
COALESCE(phone1, phone2, phone3, '无联系方式') AS contact_phone,
COALESCE(preferred_address, work_address, home_address) AS delivery_address
FROM contacts;
3. 在 WHERE 子句中使用 CASE
sql
-- 动态过滤条件
SELECT *
FROM products
WHERE
category_id = CASE
WHEN @user_type = 'VIP' THEN 1 -- VIP只能看特定类别
ELSE category_id
END
AND price <= CASE
WHEN @user_budget IS NOT NULL THEN @user_budget
ELSE 100000 -- 默认预算
END;
总结
函数/表达式 | 适用数据库 | 用途说明 | 示例 |
---|---|---|---|
CASE | 所有数据库 | 通用的条件逻辑处理 | CASE WHEN score > 90 THEN 'A' END |
IF() | MySQL | 简单的三元运算 | IF(age > 18, '成人', '未成年') |
IIF() | SQL Server | 简单的三元运算 | IIF(salary > 5000, '高', '低') |
DECODE() | Oracle | 值匹配转换 | DECODE(status, 1, '激活', 0, '未激活') |
COALESCE() | 多数据库 | 返回第一个非NULL值 | COALESCE(phone, email, '无') |
NULLIF() | 多数据库 | 两值相等返回NULL | NULLIF(salary, 0) |
核心建议:
- CASE 表达式是最通用、最强大的选择,在所有数据库中都有良好支持
- 根据使用的数据库系统选择相应的特有函数
- 保持流程控制逻辑的简洁性和可读性
- 在复杂业务逻辑中,考虑将部分逻辑移到应用层处理
这些流程控制函数使得 SQL 不再是简单的数据检索语言,而是能够处理复杂业务逻辑的强大工具。