【问题标题】:How to execute trigger and procedure in oracleoracle如何执行触发器和过程
【发布时间】:2021-04-30 03:21:38
【问题描述】:

我正在开发我的项目“超市计费管理系统”,因为我是初学者,在制作项目时遇到了很多问题。在这里,我已经创建了一个触发器和一个过程,但我不知道如何执行它,我为单个产品的总价格创建了一个触发器,即ProdTotal = ProdPrice * ProdQuantity;。 这意味着每当用户在 Products 表中输入一些数据时,必须执行此触发器,但我不知道如何执行它,类似地,我创建了一个程序来计算购买的所有产品的总价格一个客户。就像您去超市或任何商店时,购买商品后,您会收到一张账单,并且有最终的总金额。我什至不确定我的程序代码是对还是错,虽然它创建成功,但我不确定它是否会给我想要的确切输出,所以如果你能帮助我,请告诉我,我从不同的网站和许多 youtube 视频中进行了很多研究,但严重的是我不知道如何解决它,所以请帮助我!

代码:

产品表

create table Products
    ( ProdId number primary key,
      ProdNum number not null unique,
      ProdName varchar2(15),
      ProdPrice int,
      ProdQuantity int,
      ProdCustId int references Customers,
      ProdOrdId int references Orders,
      ProdStoreId int references Stores
   );

付款表

create table Payments
    ( PayId int primary key,
      PayDate date,
      ProdTotal int,
      FinalTotal int,
      PayOrdId int references orders,
      PayProdId int references Products,
      PayCustId int references Customers
    );

触发码

create trigger PROD_TOTAL
    AFTER INSERT ON Products
    BEGIN
    UPDATE Payments
    SET ProdTotal = (SELECT Products.ProdPrice * Products.ProdQuantity FROM Products);
    END;
    /

程序代码

create procedure FINAL_TOTAL(C IN NUMBER, T OUT NUMBER)
    IS
    BEGIN
    UPDATE Payments
    SET FinalTotal = FinalTotal + ProdTotal
    WHERE PayCustId = C;
    Commit;
    SELECT FinalTotal into T FROM Payments WHERE PayCustId = C;
    END;
    /

在产品表中插入语句:

insert into Products values(1,1001,'Syrup',30,4,1,1,1);

在 Payments 表中插入语句:

insert into Payments(PayId, PayDate, PayOrdID, PayProdId, PayCustId)
values(1,date'2020-10-07',1,1,1);

输出:

select * from products;

    PRODID    PRODNUM PRODNAME         PRODPRICE PRODQUANTITY PRODCUSTID
---------- ---------- --------------- ---------- ------------ ----------
 PRODORDID PRODSTOREID
---------- -----------
         1       1001 Syrup                   30            4          1
         1           1

select * from Payments;

     PAYID PAYDATE    PRODTOTAL FINALTOTAL   PAYORDID  PAYPRODID  PAYCUSTID
---------- --------- ---------- ---------- ---------- ---------- ----------
         1 07-OCT-20                                1          1          1

                                               

现在在这里,您可以看到 PRODTOTAL 和 FINALTTOTAL 列是空白的,我知道为什么它是空白的,因为我没有输入任何值。我没有在这两列中输入任何值的原因是我想要,系统应该在触发器和过程的帮助下自动计算那个计算,我什至不能删除触发器和过程,因为它在我们的强制性项目使用这两个概念。所以请帮帮我!!!

【问题讨论】:

  • prod_total 不应该在products 表中吗?这使得触发器更简单,尽管实际上您可以使用虚拟列而不是触发器。似乎插入payments 应该在过程中(而不是更新);然后您会调用该过程而不是手动插入?但目前尚不清楚您实际上被要求做什么。
  • 是的,你是对的,我什至可以在 products 表中创建一个 prod_total 列但是,我也想在我的 Payments 表中创建相同的列,所以有什么方法可以复制列的值从不同的表到另一个?
  • 试图存储一个可以在运行时计算的值是一个严重的设计缺陷。存储的值与计算的值不同只是时间问题。尝试在两个不同的地方存储相同的值也是如此。
  • @EdStevens 那么,你能告诉我任何解决方案吗
  • 最佳解决方案需要对您的实体和数据元素进行彻底分析。所有表格都应设计为第三范式。从您的 PRODUCTS 表开始。产品的价格是可以变化的,所以不应该是 PRODUCTS 的固定属性。客户、订单和商店也不是 PRODUCTS 的财产。正确进行数据设计,大多数编码问题都会消失。并且不要将业务逻辑放在触发器中。

标签: oracle stored-procedures triggers


【解决方案1】:

如前所述,首先尝试根据您的要求进行正确的设计。您可以通过正确设计数据库架构来实现许多约束。

尽可能远离触发器和 PL/SQL。它最终会迫使您进行更好的设计并获得回报。

在将触发器用于业务逻辑之前,请尝试将视图用于可以选择的事物。这就是数据库的用途。

当您“完成”后,测试性能,如果它不是最理想的,请改进您的架构。如果没有任何帮助,请开始为业务逻辑使用触发器。

我已将sample 与我正在谈论的观点放在一起。我希望它能让你开始。

create table Products (
  ProdId number generated always as identity primary key
, ProdName varchar2(20) not null
);

create table Stores (
  StoreId number generated always as identity primary key
, StoreName varchar2(20) not null
);

create table Customers (
  CustomerId number generated always as identity primary key
, CustomerName varchar2(20) not null
);

create table Prices (
  PriceId number generated always as identity primary key
, ProdId number not null
, Price number
, ValidFrom date default on null sysdate
, constraint fk_Prices_Product foreign key (ProdId) references Products (ProdId)
);

create unique index uniq_prices_product_price on Prices (ProdId, ValidFrom);

create table Orders (
  OrderId number generated always as identity primary key
, CustomerId number not null
, StoreId number not null
, OrderedAt date default on null sysdate
, constraint fk_Orders_Customer foreign key (CustomerId) references Customers (CustomerId)
, constraint fk_Orders_Store foreign key (StoreId) references Stores (StoreId)
);

create table OrderLines (
  OrderLineId number generated always as identity primary key
, OrderId number not null
, ProdId number not null
, ProdQuantity number not null
, constraint fk_OrderLines_Order foreign key (OrderId) references Orders (OrderId)
, constraint fk_OrderLines_Prod foreign key (ProdId) references Products (ProdId)
);

create table Payments (
  PaymentId number generated always as identity primary key
, OrderId number not null
, PaidAt date default on null sysdate
, PaidAmount number not null
, constraint fk_Payments_Order foreign key (OrderId) references Orders (OrderId)
);

create view Prices_V as
select
  p.*
, coalesce(
    lead(p.ValidFrom) over (partition by p.ProdId order by p.ValidFrom)
  , to_date('9999', 'YYYY')
  ) ValidTo
from Prices p;

create view Orders_V as
select
  o.*
, (
    select sum(ol.ProdQuantity * p.Price)
    from OrderLines ol
    join Prices_V p on (p.ProdId = ol.ProdId and o.OrderedAt between p.ValidFrom and p.ValidTo)
    where o.OrderId = ol.OrderId
  ) Total
, (
    select sum(PaidAmount)
    from Payments p
    where p.OrderId = o.OrderId
  ) TotalPaid
from Orders o;

insert into Products(ProdName)
select 'Prod A' from dual union all
select 'Prod B' from dual;

insert into Stores(StoreName) values ('Store A');

insert into Customers(CustomerName) 
select 'Customer A' from dual union all
select 'Customer B' from dual;

insert into Prices(ProdId, Price, ValidFrom)
select 1, 10, sysdate - 10 from dual union all
select 1, 12, sysdate - 2 from dual union all
select 1, 14, sysdate + 3 from dual union all
select 2, 100, sysdate - 10 from dual union all
select 2,  90, sysdate - 2 from dual union all
select 2,  null, sysdate + 5 from dual;

insert into Orders(CustomerId, StoreId, OrderedAt)
select 1 cid, 1 stoid, sysdate - 5 from dual union all
select 2, 1, sysdate - 5 from dual union all
select 2, 1, sysdate - 1 from dual;

insert into OrderLines(OrderId, ProdId, ProdQuantity)
select 1 ordid, 1 prodid, 3 prodquant from dual union all
select 1, 2, 2 from dual union all
select 2, 2, 10 from dual union all
select 3, 2, 10 from dual;

insert into Payments(OrderId, PaidAmount) values (2, 500);


select * from Prices_V order by ProdId, ValidFrom;
select * from OrderLines order by OrderId, ProdId;
select * from Orders_v order by OrderId;

里面的一些想法:

  1. 价格存储在单独的表中,参考产品并具有有效性,因此产品价格可以随时间变化。价格视图已添加 ValidTo 列,因此更易于使用
  2. 价格有一个唯一索引,因此我们不能同时为同一产品提供 2 个价格
  3. 您可以按顺序排列许多项目,这就是为什么OrdersOrderLines 表是一对多关系的原因
  4. Order_V 中显示总支付(使用Payments 上的子查询)并显示总订单值(使用OrderLinesPrices 上的子查询,订单日期用于获取价格表正确的时期)

根据架构,您将确定哪些事物可以表示,哪些不能。让它符合你的要求是你的工作:)

现在我明白了你所说的触发器和程序在你的项目中是强制性的。因此我有一个建议:

  1. 创建允许用户为产品创建新价格的过程。它绝对应该检查有效性不是从过去开始的。然后实现另一个允许更改有效日期的方法(也不能在过去结束)。然后,您可以撤销对 Products 表的任何插入/更新权限,并强制用户使用包含此业务逻辑的过程。
  2. 创建一个表 PricesLog 并在 Prices 上触发,这将在价格表的任何插入/更新时将 PriceId、old.Price、new.Price、sysdate 和 User 插入到日志中。

【讨论】:

    猜你喜欢
    • 2015-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-30
    • 2020-05-20
    • 1970-01-01
    • 2020-02-13
    • 2010-12-15
    相关资源
    最近更新 更多