基于触发器函数

时间:2015-12-05 22:04:24

标签: sql postgresql triggers

现在我正在编写一个具有PostgreSQL DB的应用程序。 我的任务是根据new.id编写将执行SQL语句的触发器。

只有一个问题,即触发器不执行查询。它只是插入任何东西。

DB output

CREATE OR REPLACE FUNCTION bill_creator()
RETURNS TRIGGER AS $build_bill$
declare
   summ   INTEGER;
BEGIN
   SELECT SUM(ITEMS.PRICE) INTO summ
   FROM ITEMS
   INNER JOIN PARTS
     ON PARTS.ITEM_ID = ITEMS.ID AND PARTS.ORDER_ID = NEW.ID;

    INSERT INTO BILLS (
        created,
        summary,
        cashier_id,
        customer_id,
        order_id,
        created_at,
        updated_at,
        options
    ) 
    VALUES (
        current_date,
        summ,
        1,
        new.customer_id,
        new_id,
        current_timestamp,
        current_timestamp,
        'None'
    );
    RETURN NEW;
END;
$build_bill$ LANGUAGE plpgsql;

CREATE OR REPLACE TRIGGER build_bill AFTER INSERT ON ORDERS
FOR EACH ROW EXECUTE PROCEDURE bill_creator();

我正在使用Rails,因此表由ActiveRecords创建,但它们存储在架构中。

create_table "bills", force: :cascade do |t|
  t.datetime "created"
  t.string   "options"
  t.integer  "summary"
  t.integer  "cashier_id"
  t.integer  "customer_id"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
  t.integer  "order_id"
end

create_table "orders", force: :cascade do |t|
  t.integer  "customer_id"
  t.integer  "waiter_id"
  t.integer  "manager_id"
  t.integer  "chef_id"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
end

create_table "parts", force: :cascade do |t|
  t.integer  "order_id"
  t.integer  "item_id"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "items", force: :cascade do |t|
  t.integer  "price"
  t.string   "description"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
  t.integer  "menu_id"
end

add_index "parts", ["item_id"], name: "index_parts_on_item_id", using: :btree
add_index "parts", ["order_id"], name: "index_parts_on_order_id", using: :btree

1 个答案:

答案 0 :(得分:1)

你的功能看起来基本上很好。我简化并修复了一些问题:

CREATE OR REPLACE FUNCTION bill_creator()
  RETURNS TRIGGER AS
$build_bill$
BEGIN
   INSERT INTO bills (
     created,
     summary,
     cashier_id,
     customer_id,
     order_id,
     created_at,
     updated_at,
     options
   ) 
   SELECT
     current_date,
     SUM(i.price)
     1,
     NEW.customer_id,
     NEW.id              -- ! 'new_id was undefined !
     current_timestamp,
     current_timestamp,
     'None'
   FROM   parts p
   JOIN   items i ON p.item_id = i.id
   WHERE  p.order_id = NEW.id;

   RETURN NULL;
END
$build_bill$  LANGUAGE plpgsql;

您不需要单独的SELECT和变量赋值。将其集成到单个INSERT中,速度更快。结果相同,即使找不到任何内容,聚合函数sum()也总是返回一行。

new_id未定义,我认为您的意思是NEW.id

我使用RETURN NULL因为per documentation:

  

始终忽略已触发的行级触发器的返回值AFTER [...];它也可能是空的。

不确定为什么你有createdcreated_at。两者都是类型定义的AS t.datetime,它应转换为Postgres timestamp。也不确定为什么要将current_date而不是current_timestamp写入第一个。

但是,插入的summary为NULL并不奇怪。在orders中插入一行后,parts中不存在相关条目,但假设参照完整性。因此,此时在bill中插入总和的想法必然会失败。对于插入/更新/删除的每个新UPDATE,您必须part该列。这是一袋你必须处理的跳蚤来保持这个总和 - 或多或少......

我建议您完全删除列bill.summary ,然后使用VIEW代替动态计算总和。可以是MATERIALIZED VIEW以避免重复聚合。