数据库开发实战案例分享:从函数到存储过程的应用

这个数据库实验案例展示了如何使用 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;
/
相关推荐
北京中邦兴业2 小时前
GMP洁净环境监测法规深度解读:构建以风险为核心的动态防御体系
数据库·人工智能·面试·职场和发展
TDengine (老段)2 小时前
人力减 60%:时序数据库 TDengine 助力桂冠电力实现 AI 智能巡检
java·大数据·数据库·人工智能·时序数据库·tdengine·涛思数据
潇I洒2 小时前
Ubuntu Linux 24.04 安装MySQL 8.4.7
linux·数据库·mysql·ubuntu
milanyangbo2 小时前
像Git一样管理数据:深入解析数据库并发控制MVCC的实现
服务器·数据库·git·后端·mysql·架构·系统架构
云和恩墨2 小时前
制造企业跨工厂数据库集中管理:3 步破解异构环境同步与监控难题
数据库
半夏知半秋2 小时前
MongoDB 与 Elasticsearch 数据同步方案整理
大数据·数据库·mongodb·elasticsearch·搜索引擎
虎头金猫2 小时前
openEuler 22.03 LTS 时序数据库实战:InfluxDB 深度性能评测与优化指南
网络·数据库·python·网络协议·tcp/ip·负载均衡·时序数据库
菜鸟小九2 小时前
mysql运维(读写分离)
运维·数据库·mysql
菜鸟小九3 小时前
mysql运维(分库分表)
运维·数据库·mysql