overmind 2023-09-30 08:24 采纳率: 90.9%
浏览 8
已结题

关于PROCEDURE和FUNCTION的问题

CREATE TABLE public.bill (
    ic_id character varying(10),
    dk_id character varying(10),
    deal_time timestamp without time zone,
    price numeric
);
CREATE TABLE public.stall (
    dk_id character varying(10),
    shop character varying(20),
    shop_owner character varying(20)
);

COPY public.bill (ic_id, dk_id, deal_time, price) FROM stdin;
ic2034123    dk1000234    2023-09-30 11:30:01    15
ic2052342    dk1001584    2023-09-30 11:33:03    30
ic2034123    dk1001584    2023-09-30 11:34:04    3
ic2033233    dk1003324    2023-09-30 11:36:23    20
ic2035678    dk1000436    2023-09-30 11:36:44    20
ic2034434    dk1001584    2023-09-30 11:37:51    25
ic1034139    dk1003324    2023-09-30 11:38:23    20
\.

COPY public.stall (dk_id, shop, shop_owner) FROM stdin;
dk1000436    饭是钢    胡小喵
dk1000234    兰州拉面    刘老根
dk1003324    煎饼虎头军    胡小喵
dk1001584    老家肉饼    刘老根
\.
  • 上述代码生成两个表
    • bill 表 记录 消费的ic卡、档口id、金额、时间
    • stall 表 记录 档口的具体品牌和老板

img

  • 现在想要插入记录

    • ic1003532,dk1001584,2023-10-01,10:00:00,10,老家肉饼,刘老根
    • 由于刘老根的老家肉饼店铺的dk_id已存在,只插入账单表(bill)
  • 如果有一个新的档口,比如

    • ic1003533,dk1001589,2023-10-01,11:00:00,10,沙县小吃,沙老板
    • 由于新档口 不存在 需要在两个表插入记录
    • 这应该 建立一个FUNCTION 还是 PROCEDURE?
      • 我对于这两者区别 不是很清楚
      • 具体应该怎么写?
  • 删除店铺(stall)记录时,想要制作一个触发器TRIGGER

    • 连带删除所有bill中相关记录
    • 具体应该怎么写?
  • 谢谢指教!

  • 写回答

7条回答 默认 最新

  • 专家-赤兔[在线] 2023-09-30 08:29
    关注

    以下回复引自强大而免费的微信小程序:皆我百晓生。如有帮助,还望采纳。


    方法1:
    在这种情况下,可以使用一个FUNCTION来实现插入记录的操作。FUNCTION是一种可以返回值的数据库对象,可以用于执行特定的计算和操作。对于要插入的新档口,可以在函数中使用条件语句来判断该档口是否已存在,如果不存在,则在stall表中插入新记录,并在bill表中插入新的账单记录。如果档口已经存在,则只需要在bill表中插入新的账单记录即可。

    示例代码如下:

    CREATE FUNCTION insert_record(ic_id character varying, dk_id character varying, deal_date date, deal_time time, price numeric, shop character varying, shop_owner character varying)
    RETURNS void AS $$
    BEGIN
        IF NOT EXISTS(SELECT 1 FROM stall WHERE dk_id = dk_id) THEN
            INSERT INTO stall (dk_id, shop, shop_owner)
            VALUES (dk_id, shop, shop_owner);
        END IF;
        
        INSERT INTO bill (ic_id, dk_id, deal_time, price)
        VALUES (ic_id, dk_id, TIMESTAMP deal_date || ' ' || deal_time, price);
    END;
    $$ LANGUAGE plpgsql;
    

    对于删除店铺记录后删除关联的bill记录,可以使用触发器TRIGGER来实现。触发器是一种特殊的数据库对象,可以在表上定义,以在表上的特定操作(如INSERT、DELETE、UPDATE)发生时自动触发。可以在删除店铺记录之前创建一个DELETE触发器,当删除店铺记录时,触发器会在执行删除操作之前删除所有关联的bill记录。

    示例代码如下:

    CREATE FUNCTION delete_related_bills() RETURNS TRIGGER AS $$
    BEGIN
        DELETE FROM bill WHERE dk_id = OLD.dk_id;
        RETURN OLD;
    END;
    $$ LANGUAGE plpgsql;
    
    CREATE TRIGGER delete_related_bills_trigger
    BEFORE DELETE ON stall
    FOR EACH ROW
    EXECUTE FUNCTION delete_related_bills();
    

    通过上述代码,当执行删除店铺记录的操作时,将会触发delete_related_bills_trigger触发器,触发器会在删除店铺记录之前删除所有关联的bill记录。


    方法2:
    对于插入记录的需求,可以使用一个存储过程(PROCEDURE)来实现。存储过程可以执行一系列的 SQL 语句,可以在其中进行必要的条件判断和数据插入操作。

    以下是一个示例存储过程的代码片段,用于根据给定的参数插入数据到 bill 表:

    CREATE OR REPLACE PROCEDURE insert_bill(
         ic_id_val character varying(10),
         dk_id_val character varying(10),
         date_val date,
         time_val time,
         price_val numeric,
         shop_val character varying(20),
         shop_owner_val character varying(20)
    )
    LANGUAGE plpgsql
    AS $$
    BEGIN
         -- 检查档口是否存在
         IF EXISTS (SELECT * FROM stall WHERE dk_id = dk_id_val) THEN
             -- 档口存在,只插入 bill 表
             INSERT INTO bill (ic_id, dk_id, deal_time, price)
             VALUES (ic_id_val, dk_id_val, date_val + time_val, price_val);
         ELSE
             -- 档口不存在,先插入 stall 表,再插入 bill 表
             INSERT INTO stall (dk_id, shop, shop_owner)
             VALUES (dk_id_val, shop_val, shop_owner_val);
       
             INSERT INTO bill (ic_id, dk_id, deal_time, price)
             VALUES (ic_id_val, dk_id_val, date_val + time_val, price_val);
         END IF;
    END;
    $$;
    

    使用该存储过程可以执行以下操作:

    CALL insert_bill('ic1003532', 'dk1001584', '2023-10-01', '10:00:00', 10, '老家肉饼', '刘老根');
    

    对于删除店铺记录并触发连带删除相关的 bill 记录的需求,可以使用触发器(TRIGGER)来实现。

    以下是一个示例触发器的代码片段,用于在删除 stall 表记录时同时删除 bill 表相关记录:

    CREATE OR REPLACE FUNCTION delete_related_bills()
    RETURNS TRIGGER AS $$
    BEGIN
         DELETE FROM bill WHERE dk_id = OLD.dk_id;
         RETURN OLD;
    END;
    $$ LANGUAGE plpgsql;
    
    CREATE TRIGGER delete_related_bills_trigger
    AFTER DELETE ON stall
    FOR EACH ROW
    EXECUTE FUNCTION delete_related_bills();
    

    创建完触发器后,每当删除 stall 表的记录时,相关的 bill 表记录也会被自动删除。

    注意:以上代码只是示例,您需要根据实际的表结构和需求进行适当的调整和修改。还要确保在执行任何更改之前进行适当的备份,以免意外删除了重要的数据。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
  • 忧伤的玩不起 2023-09-30 08:56
    关注

    个人觉得实际用的过程中存储过程和函数用的也不多,函数就用一些很简单嗯操作.
    一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强。 存储过程,功能强大,可以执行包括修改表等一系列数据库操作;用户定义函数不能用于执行一组修改全局数据库状态的操作。
    对于存储过程来说可以返回参数,如记录集,而函数只能返回值或者表对象。函数只能返回一个变量;而存储过程可以返回多个。存储过程的参数可以有IN,OUT,INOUT三种类型,而函数只能有IN类型。存储过程声明时不需要返回类型,而函数声明时需要描述返回类型,且函数体中必须包含一个有效的RETURN语句。
    存储过程一般是作为一个独立的部分来执行( EXECUTE 语句执行),而函数可以作为查询语句的一个部分来调用(SELECT调用),由于函数可以返回一个表对象,因此它可以在查询语句中位于FROM关键字的后面。 SQL语句中不可用存储过程,而可以使用函数。
    第一和第二个问题用下面可以一起解决

    CREATE OR REPLACE FUNCTION insert_records()  
    RETURNS trigger AS 
    $$
    DECLARE  
         stall_row stall%ROWTYPE;  
    BEGIN  
        SELECT * INTO stall_row FROM stall WHERE dk_id = NEW.dk_id;  
          
        IF NOT FOUND THEN  
            -- Stall row not found, insert into stall first  
            INSERT INTO stall (dk_id, shop, shop_owner) VALUES (NEW.dk_id, NEW.shop, NEW.shop_owner);  
        END IF;  
          
        -- Then insert into bill  
        INSERT INTO bill (ic_id, dk_id, deal_time, price) VALUES (NEW.ic_id, NEW.dk_id, NEW.deal_time, NEW.price);  
       
        RETURN NEW;  
    END;  
    
    $$
     LANGUAGE plpgsql;
    

    第三个问题:
    对于你的第三个问题,如果你想要在删除stall表的记录时也删除所有与之相关的bill表的记录,你可以创建一个DELETE触发器,如下:

    CREATE TRIGGER delete_stall_trigger  
    AFTER DELETE ON stall  
    FOR EACH ROW EXECUTE FUNCTION delete_bill_records();
    

    你需要定义一个函数delete_bill_records(),它会删除所有与刚删除的stall记录相关的bill记录:

    CREATE OR REPLACE FUNCTION delete_bill_records()  
    RETURNS trigger AS 
    $$
      
    DECLARE  
    BEGIN  
        DELETE FROM bill WHERE dk_id = OLD.dk_id;  
       
        RETURN OLD;  
    END;  
    
    $$
     LANGUAGE plpgsql;
    
    
    评论 编辑记录
  • 数据大魔王 2023-09-30 10:01
    关注

    用一个存储过程(PROCEDURE)会更合适。
    代码:

    CREATE OR REPLACE FUNCTION insert_bill_and_stall(
        ic_id character varying(10),
        dk_id character varying(10),
        deal_date date,
        deal_time time,
        price numeric,
        shop character varying(20),
        shop_owner character varying(20)
    )
    RETURNS void AS $$
    BEGIN
        -- 检查档口是否已存在
        IF EXISTS (SELECT 1 FROM public.stall WHERE dk_id = dk_id) THEN
            -- 如果存在,只插入账单表
            INSERT INTO public.bill (ic_id, dk_id, deal_time, price) VALUES (ic_id, dk_id, TIMESTAMP (deal_date || ' ' || deal_time), price);
        ELSE
            -- 如果不存在,插入档口表和账单表
            INSERT INTO public.stall (dk_id, shop, shop_owner) VALUES (dk_id, shop, shop_owner);
            INSERT INTO public.bill (ic_id, dk_id, deal_time, price) VALUES (ic_id, dk_id, TIMESTAMP (deal_date || ' ' || deal_time), price);
        END IF;
    END;
    $$ LANGUAGE plpgsql;
    

    要调用这个存储过程,用以下代码:

    SELECT insert_bill_and_stall('ic1003532', 'dk1001584', '2023-10-01', '10:00:00', 10, '老家肉饼', '刘老根');
    

    为了实现删除店铺记录时连带删除所有相关账单记录的功能,你可以使用一个触发器(TRIGGER)。在删除的时候触发器会检查删除的店铺有哪些相关的账单记录,并将其删除。

    删除店铺记录时的触发器的代码:

    CREATE OR REPLACE FUNCTION delete_related_bills()
    RETURNS TRIGGER AS $$
    BEGIN
        DELETE FROM public.bill WHERE dk_id = OLD.dk_id;
        RETURN OLD;
    END;
    $$ LANGUAGE plpgsql;
    
    CREATE TRIGGER after_delete_stall
    AFTER DELETE ON public.stall
    FOR EACH ROW
    EXECUTE FUNCTION delete_related_bills();
    
    评论
  • 杨得江-君临天下wyj 2023-09-30 10:02
    关注
    
    CREATE OR REPLACE FUNCTION insert_bill_and_stall(
        ic_id character varying(10),
        dk_id character varying(10),
        deal_date date,
        deal_time time,
        price numeric,
        shop character varying(20),
        shop_owner character varying(20)
    )
    RETURNS void AS $$
    BEGIN
        -- 检查档口是否已存在
        IF EXISTS (SELECT 1 FROM public.stall WHERE dk_id = dk_id) THEN
            -- 如果存在,只插入账单表
            INSERT INTO public.bill (ic_id, dk_id, deal_time, price) VALUES (ic_id, dk_id, TIMESTAMP (deal_date || ' ' || deal_time), price);
        ELSE
            -- 如果不存在,插入档口表和账单表
            INSERT INTO public.stall (dk_id, shop, shop_owner) VALUES (dk_id, shop, shop_owner);
            INSERT INTO public.bill (ic_id, dk_id, deal_time, price) VALUES (ic_id, dk_id, TIMESTAMP (deal_date || ' ' || deal_time), price);
        END IF;
    END;
    $$ LANGUAGE plpgsql;
    
    
    评论
  • 心梓知识 2023-09-30 12:40
    关注

    结合GPT给出回答如下请题主参考
    PROCEDURE和FUNCTION都是数据库中存储过程的类型,二者都可以用来封装一系列的SQL语句,以便在需要时直接调用。

    下面给出一个样例案例:

    创建一个名为get_total_amount的FUNCTION,该函数接受一个ic_id和一个dk_id,并返回指定ic_id和dk_id的账单总金额。

    CREATE OR REPLACE FUNCTION get_total_amount(ic_id character varying(10), dk_id character varying(10))
    RETURNS numeric
    LANGUAGE plpgsql
    AS $$
    DECLARE
      total_amount numeric;
    BEGIN
      SELECT SUM(dea) INTO total_amount
      FROM public.bill
      WHERE ic_id = get_total_amount.ic_id AND dk_id = get_total_amount.dk_id;
      RETURN total_amount;
    END;
    $$;
    

    在上述函数中,我们利用了SELECT SUM语句计算账单总金额,并将结果保存在total_amount变量中。最后,我们返回total_amount的值作为函数结果。

    使用该函数的示例代码:

    SELECT get_total_amount('ic001', 'dk001');
    

    以上代码将返回ic_id为"ic001"和dk_id为"dk001"的账单总金额。

    相比之下,PROCEDURE与FUNCTION的不同之处在于,PROCEDURE不返回任何值,而FUNCTION则始终返回一个值。以下是一个名为update_balance的PROCEDURE的示例,该存储过程将在账单表中更新指定ic_id和dk_id的账户余额:

    CREATE OR REPLACE PROCEDURE update_balance(ic_id character varying(10), dk_id character varying(10))
    LANGUAGE plpgsql
    AS $$
    BEGIN
      UPDATE public.bill SET dea = 0 WHERE ic_id = update_balance.ic_id AND dk_id = update_balance.dk_id;
    END;
    $$;
    

    在此示例中,我们利用了UPDATE语句将账单表中指定ic_id和dk_id的账户余额更新为0。与上述示例不同的是,该PROCEDURE不返回任何结果,而只是对数据库进行了修改。

    评论
  • yy64ll826 2023-09-30 16:10
    关注
    评论
  • coder_small_bell 2023-09-30 23:23
    关注

    用存储过程当函数

    评论
查看更多回答(6条)

报告相同问题?

问题事件

  • 系统已结题 10月10日
  • 已采纳回答 10月2日
  • 创建了问题 9月30日

悬赏问题

  • ¥30 哈夫曼编码译码器打印树形项目
  • ¥20 求完整顺利登陆QQ邮箱的python代码
  • ¥15 怎么下载MySQL,怎么卸干净原来的MySQL
  • ¥15 网络打印机Ip地址自动获取出现问题
  • ¥15 求局部放电案例库,用于预测局部放电类型
  • ¥100 QT Open62541
  • ¥15 stata合并季度数据和日度数据
  • ¥15 谁能提供rabbitmq,erlang,socat压缩包,记住版本要对应
  • ¥15 Vue3 中使用 `vue-router` 只能跳转到主页面?
  • ¥15 用QT,进行QGIS二次开发,如何在添加栅格图层时,将黑白的矢量图渲染成彩色