这个数据库实验案例展示了如何使用 Oracle 数据库完成一个完整的商品销售与会员分析系统。通过几个简洁的步骤,它涵盖了数据库开发中的核心技能点。
一、核心设计
案例从基础数据建模开始:
-
创建商品表(
product)记录商品基本信息与采购价 -
建立销售清单表(
sales_list)关联会员与商品,记录每笔销售
二、技术实现亮点
1. 业务逻辑封装为函数
通过 get_member_level() 函数,将会员等级判断逻辑(根据消费金额划分普通、银卡、金卡、钻石会员)封装起来,提高代码复用性。
2. 动态表生成存储过程
create_member_level_table() 存储过程能按月自动创建会员等级分析表,动态处理表名(如 member_level_10),并先判断删除已存在的表,避免冲突。
3. 游标驱动的报表生成
get_prod_profit_by_month() 使用游标逐行计算指定月份各商品的利润(销售价减采购价),并以清晰格式输出统计结果。
三、实际应用价值
这个案例虽小,却完整展示了:
-
数据分层:基础数据 → 聚合分析 → 报表输出
-
代码模块化:函数处理单一逻辑,存储过程组织复杂操作
-
动态SQL应用:根据参数动态创建数据库对象
-
异常处理:包含基本的错误捕获与反馈机制
四、详细语句
sql
-- 1.1 创建商品表
CREATE TABLE product (
prod_id VARCHAR2(4) PRIMARY KEY,
prod_name VARCHAR2(20) NOT NULL,
purchase_price NUMBER(8,2) CHECK (purchase_price > 0)
);
-- 1.2 插入商品数据
INSERT INTO product VALUES ('P001', '瓶装洗衣液', 25.00);
INSERT INTO product VALUES ('P002', '纯棉毛巾礼盒', 45.00);
INSERT INTO product VALUES ('P003', '进口零食大礼包', 60.00);
INSERT INTO product VALUES ('P004', '不锈钢保温杯', 30.00);
INSERT INTO product VALUES ('P005', '厨房清洁套装', 35.00);
COMMIT;
-- 1.3 创建销售清单表(含外键关联商品表)
CREATE TABLE sales_list (
sale_id VARCHAR2(4) PRIMARY KEY,
member_id VARCHAR2(4) NOT NULL,
sale_date DATE NOT NULL,
prod_id VARCHAR2(4) NOT NULL,
sale_price NUMBER(8,2) CHECK (sale_price > 0),
CONSTRAINT fk_sales_prod FOREIGN KEY (prod_id) REFERENCES product(prod_id)
);
-- 1.4 插入销售数据
INSERT INTO sales_list VALUES ('S001', 'M001', TO_DATE('2024-09-05', 'YYYY-MM-DD'), 'P001', 39.90);
INSERT INTO sales_list VALUES ('S002', 'M002', TO_DATE('2024-09-12', 'YYYY-MM-DD'), 'P002', 69.90);
INSERT INTO sales_list VALUES ('S003', 'M001', TO_DATE('2024-09-20', 'YYYY-MM-DD'), 'P003', 89.90);
INSERT INTO sales_list VALUES ('S004', 'M003', TO_DATE('2024-10-08', 'YYYY-MM-DD'), 'P004', 49.90);
INSERT INTO sales_list VALUES ('S005', 'M002', TO_DATE('2024-10-15', 'YYYY-MM-DD'), 'P005', 59.90);
INSERT INTO sales_list VALUES ('S006', 'M004', TO_DATE('2024-10-22', 'YYYY-MM-DD'), 'P001', 38.00);
INSERT INTO sales_list VALUES ('S007', 'M003', TO_DATE('2024-11-03', 'YYYY-MM-DD'), 'P002', 65.00);
INSERT INTO sales_list VALUES ('S008', 'M001', TO_DATE('2024-11-10', 'YYYY-MM-DD'), 'P004', 45.00);
INSERT INTO sales_list VALUES ('S009', 'M004', TO_DATE('2024-11-18', 'YYYY-MM-DD'), 'P003', 65.00);
COMMIT;
-- 2.1 创建会员等级判断函数
CREATE OR REPLACE FUNCTION get_member_level(p_total_cost NUMBER)
RETURN VARCHAR2
IS
v_level VARCHAR2(10);
BEGIN
IF p_total_cost < 30 THEN
v_level := '普通会员';
ELSIF p_total_cost < 50 THEN
v_level := '银卡会员';
ELSIF p_total_cost < 80 THEN
v_level := '金卡会员';
ELSE
v_level := '钻石会员';
END IF;
RETURN v_level;
END;
/
-- 设置列宽(根据实际字段长度调整,例如会员编号设为8,总消费设为10,会员等级设为10)
COLUMN 会员编号 FORMAT A8
COLUMN 总消费 FORMAT 999.99
COLUMN 会员等级 FORMAT A10
-- 3.1 SQL查询调用函数(查看所有会员总消费+等级)
SELECT
member_id AS 会员编号,
SUM(sale_price) AS 总消费,
get_member_level(SUM(sale_price)) AS 会员等级
FROM sales_list
GROUP BY member_id;
-- 3.2 PL/SQL块调用函数(测试指定金额的等级)
-- 1. 先开启服务器输出(必做,否则DBMS_OUTPUT不会显示内容)
SET SERVEROUTPUT ON;
-- 2. 执行PL/SQL块
DECLARE
v_test_cost NUMBER := 60;
v_test_level VARCHAR2(10);
BEGIN
v_test_level := get_member_level(v_test_cost); -- 调用函数
DBMS_OUTPUT.PUT_LINE('消费' || v_test_cost || '元对应的会员等级是:' || v_test_level);
END;
/
-- 4.1 查询2024年10月份会员总消费+等级
SELECT
member_id AS 会员编号,
SUM(sale_price) AS 总消费,
get_member_level(SUM(sale_price)) AS 会员等级
FROM sales_list
WHERE TO_CHAR(sale_date, 'YYYY-MM') = '2024-10'
GROUP BY member_id;
第二个实验三
-- 5.1 创建"按月生成会员等级表"存储过程
CREATE OR REPLACE PROCEDURE create_member_level_table(p_month VARCHAR2)
IS
v_table_name VARCHAR2(30);
v_month_num VARCHAR2(2);
v_table_exists NUMBER;
BEGIN
v_month_num := SUBSTR(p_month, 6, 2);
v_table_name := 'member_level_' || v_month_num;
SELECT COUNT(*) INTO v_table_exists
FROM user_tables
WHERE table_name = UPPER(v_table_name); -- Oracle表名默认大写
IF v_table_exists = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE ' || v_table_name || ' PURGE';
END IF;
EXECUTE IMMEDIATE '
CREATE TABLE ' || v_table_name || ' (
member_id VARCHAR2(4),
total_cost NUMBER(8,2),
member_level VARCHAR2(10)
)
';
EXECUTE IMMEDIATE '
INSERT INTO ' || v_table_name || '
SELECT
member_id,
SUM(sale_price),
get_member_level(SUM(sale_price))
FROM sales_list
WHERE TO_CHAR(sale_date, ''YYYY-MM'') = ''' || p_month || '''
GROUP BY member_id
';
COMMIT;
DBMS_OUTPUT.PUT_LINE('已成功创建表:' || v_table_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('创建失败:' || SQLERRM);
END;
/
存储过程调用验证
-- 6.1 调用存储过程生成2024年11月会员等级表
SET SERVEROUTPUT ON; -- 确保输出开启
BEGIN
create_member_level_table('2024-10');
END;
/
-- 6.2 验证表数据
SELECT * FROM member_level_10;
-- 1. 开启服务器输出(查看执行结果)
SET SERVEROUTPUT ON;
-- 2. 调用存储过程,创建2024年11月的会员等级表(表名:member_level_11)
BEGIN
create_member_level_table('2024-11');
END;
/
-- 3. 验证表是否创建成功,并查看数据
SELECT * FROM member_level_11;
实验五
CREATE OR REPLACE PROCEDURE get_prod_profit_by_month(p_month VARCHAR2)
IS
CURSOR cur_prod_profit IS
SELECT
p.prod_id,
p.prod_name,
SUM(s.sale_price - p.purchase_price) AS total_profit
FROM product p
JOIN sales_list s
ON p.prod_id = s.prod_id
WHERE TO_CHAR(s.sale_date, 'YYYY-MM') = p_month
GROUP BY p.prod_id, p.prod_name;
v_prod_id product.prod_id%TYPE;
v_prod_name product.prod_name%TYPE;
v_total_profit NUMBER(8,2);
BEGIN
OPEN cur_prod_profit;
DBMS_OUTPUT.PUT_LINE('===== ' || p_month || ' 商品利润统计 =====');
DBMS_OUTPUT.PUT_LINE('商品编号 | 商品名称 | 总利润');
DBMS_OUTPUT.PUT_LINE('-------------------------------');
-- 循环提取游标数据
LOOP
FETCH cur_prod_profit INTO v_prod_id, v_prod_name, v_total_profit;
EXIT WHEN cur_prod_profit%NOTFOUND;
-- 格式化输出(对齐显示)
DBMS_OUTPUT.PUT_LINE(
RPAD(v_prod_id, 8) || ' | ' ||
RPAD(v_prod_name, 15) || ' | ' ||
TO_CHAR(v_total_profit, '999.99')
);
END LOOP;
CLOSE cur_prod_profit;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('统计失败:' || SQLERRM);
IF cur_prod_profit%ISOPEN THEN
CLOSE cur_prod_profit;
END IF;
END;
/
-- 调用存储过程(统计2024年11月的商品利润)
SET SERVEROUTPUT ON;
BEGIN
get_prod_profit_by_month('2024-11');
END;
/