You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle存储过程:如何引用触发器生成的前两次插入操作ID?

解决触发器生成ID的引用与按日汇总问题

嘿,我来帮你捋清楚这个问题!你现在的核心需求是:在基于游标的存储过程里插入数据后,要引用触发器自动生成的Value和Calculation表的ID,同时还要实现按日汇总的计算。先给你两种靠谱的方案,再聊聊按日汇总的优化思路:

方案1:插入时直接捕获生成的ID(最推荐,无并发风险)

不管你用的是Oracle、SQL Server还是其他主流数据库,都支持在插入语句里直接返回触发器生成的主键ID——这种方式比事后查询最新ID靠谱太多,不会因为其他会话同时插入数据而拿到错误的ID。

以Oracle为例(适配你的存储过程)

create or replace PROCEDURE TEST_PROC IS 
  Cursor c1 is 
    SELECT SUM(v.value_tx) AS sum_of_values 
           , e.entity_id AS entity_id 
           , e.entity_name_tx AS entity_name 
           , e.ei_id_tx AS ei_id 
           , v.create_dt AS create_dt 
           , v.hr_num AS hr_num 
           , v.utc_offset 
           , v.data_date 
           , v.hr_utc 
           , v.hr 
           , ff.form_field_id
           , s.survey_respondent_id
           , v.data_code 
           , 'N' AS processed 
    FROM value v 
    join submission_value sv ON v.value_id = sv.value_id 
    join form_field ff ON sv.form_field_id = ff.form_field_id 
    join submission s ON sv.submission_id = s.submission_id 
    join survey_respondent sr ON s.survey_respondent_id = sr.survey_respondent_id 
    join entity e ON sr.entity_id = e.entity_id 
    WHERE ei_id_tx IN ('ABC', 'ZYX', 'ADA', 'AZL', 'BAN', 'CAB') 
      AND ff.form_field_id IN ('55', '77') 
    GROUP BY e.entity_id, e.entity_name_tx, e.ei_id_tx, v.create_dt, v.hr_num, v.utc_offset, v.data_date, v.hr_utc, v.hr, v.data_code; 
  l_var c1%ROWTYPE;
  l_new_value_id NUMBER; -- 存储刚插入Value表的ID
BEGIN 
  OPEN c1; 
  LOOP 
    FETCH c1 into l_var; 
    EXIT WHEN c1%NOTFOUND; 
    
    -- 插入Value表并通过RETURNING子句捕获触发器生成的ID
    INSERT INTO value (sum_of_values, entity_id, entity_name_tx, ei_id_tx, create_dt, hr_num, utc_offset, data_date, hr_utc, hr, data_code, processed)
    VALUES (l_var.sum_of_values, l_var.entity_id, l_var.entity_name, l_var.ei_id, l_var.create_dt, l_var.hr_num, l_var.utc_offset, l_var.data_date, l_var.hr_utc, l_var.hr, l_var.data_code, l_var.processed)
    RETURNING value_id INTO l_new_value_id; -- 关键:直接拿触发器生成的ID
    
    -- 现在用这个ID关联Calculation表,实现按日汇总(存在则更新,不存在则插入)
    DECLARE
      l_existing_calc_id NUMBER;
    BEGIN
      -- 先查当天是否已有该实体+数据编码的汇总记录
      SELECT calculation_id INTO l_existing_calc_id
      FROM calculation
      WHERE entity_id = l_var.entity_id
        AND data_code = l_var.data_code
        AND calculation_date = TRUNC(l_var.data_date);
        
      -- 有就更新汇总值
      UPDATE calculation
      SET daily_sum = daily_sum + l_var.sum_of_values,
          last_updated_dt = SYSDATE
      WHERE calculation_id = l_existing_calc_id;
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        -- 没有就插入新的汇总记录
        INSERT INTO calculation (value_id, daily_sum, calculation_date, entity_id, data_code)
        VALUES (l_new_value_id, l_var.sum_of_values, TRUNC(l_var.data_date), l_var.entity_id, l_var.data_code);
    END;
    
  END LOOP; 
  CLOSE c1;
END TEST_PROC;
/

如果是SQL Server,语法调整如下

CREATE PROCEDURE TEST_PROC
AS
BEGIN
  SET NOCOUNT ON;
  
  DECLARE @c1 CURSOR;
  DECLARE @sum_of_values DECIMAL(18,2), @entity_id INT, @entity_name_tx VARCHAR(100), @ei_id_tx VARCHAR(50),
          @create_dt DATETIME, @hr_num INT, @utc_offset INT, @data_date DATE, @hr_utc INT, @hr INT,
          @form_field_id VARCHAR(10), @survey_respondent_id INT, @data_code VARCHAR(50), @processed CHAR(1),
          @new_value_id INT;

  SET @c1 = CURSOR FOR
    SELECT SUM(v.value_tx) AS sum_of_values 
           , e.entity_id AS entity_id 
           , e.entity_name_tx AS entity_name 
           , e.ei_id_tx AS ei_id 
           , v.create_dt AS create_dt 
           , v.hr_num AS hr_num 
           , v.utc_offset 
           , v.data_date 
           , v.hr_utc 
           , v.hr 
           , ff.form_field_id
           , s.survey_respondent_id
           , v.data_code 
           , 'N' AS processed 
    FROM value v 
    join submission_value sv ON v.value_id = sv.value_id 
    join form_field ff ON sv.form_field_id = ff.form_field_id 
    join submission s ON sv.submission_id = s.submission_id 
    join survey_respondent sr ON s.survey_respondent_id = sr.survey_respondent_id 
    join entity e ON sr.entity_id = e.entity_id 
    WHERE ei_id_tx IN ('ABC', 'ZYX', 'ADA', 'AZL', 'BAN', 'CAB') 
      AND ff.form_field_id IN ('55', '77') 
    GROUP BY e.entity_id, e.entity_name_tx, e.ei_id_tx, v.create_dt, v.hr_num, v.utc_offset, v.data_date, v.hr_utc, v.hr, v.data_code;

  OPEN @c1;
  FETCH NEXT FROM @c1 INTO @sum_of_values, @entity_id, @entity_name_tx, @ei_id_tx, @create_dt, @hr_num, @utc_offset, @data_date, @hr_utc, @hr, @form_field_id, @survey_respondent_id, @data_code, @processed;

  WHILE @@FETCH_STATUS = 0
  BEGIN
    -- 插入Value表并通过OUTPUT子句获取ID
    INSERT INTO value (sum_of_values, entity_id, entity_name_tx, ei_id_tx, create_dt, hr_num, utc_offset, data_date, hr_utc, hr, data_code, processed)
    OUTPUT INSERTED.value_id INTO @new_value_id
    VALUES (@sum_of_values, @entity_id, @entity_name_tx, @ei_id_tx, @create_dt, @hr_num, @utc_offset, @data_date, @hr_utc, @hr, @data_code, @processed);

    -- 处理按日汇总:先查是否存在当天的汇总记录
    IF EXISTS (SELECT 1 FROM calculation WHERE entity_id = @entity_id AND data_code = @data_code AND calculation_date = CAST(@data_date AS DATE))
    BEGIN
      UPDATE calculation
      SET daily_sum = daily_sum + @sum_of_values,
          last_updated_dt = GETDATE()
      WHERE entity_id = @entity_id AND data_code = @data_code AND calculation_date = CAST(@data_date AS DATE);
    END
    ELSE
    BEGIN
      INSERT INTO calculation (value_id, daily_sum, calculation_date, entity_id, data_code)
      VALUES (@new_value_id, @sum_of_values, CAST(@data_date AS DATE), @entity_id, @data_code);
    END

    FETCH NEXT FROM @c1 INTO @sum_of_values, @entity_id, @entity_name_tx, @ei_id_tx, @create_dt, @hr_num, @utc_offset, @data_date, @hr_utc, @hr, @form_field_id, @survey_respondent_id, @data_code, @processed;
  END;

  CLOSE @c1;
  DEALLOCATE @c1;
END;
GO

方案2:通过查询获取历史ID(不推荐,有并发风险)

如果你确实想用查询的方式获取ID,一定要加精准的过滤条件,绝对不能只查MAX(value_id)——因为并发场景下,其他会话的插入会让你拿到错误的ID。比如:

-- 插入Value表后,用唯一的业务组合条件查询ID
SELECT value_id INTO l_new_value_id
FROM value
WHERE entity_id = l_var.entity_id
  AND data_date = l_var.data_date
  AND data_code = l_var.data_code
  AND create_dt = l_var.create_dt; -- 用这些唯一标识过滤,确保拿到自己插入的那条记录

这种方式的问题是,如果你的表没有为这些过滤字段建立唯一索引,可能会查到多条记录,而且高并发下容易出错,所以优先用方案1。

关于按日汇总的额外建议

如果你的按日汇总数据量比较大,建议给calculation表的entity_id + data_code + calculation_date建立唯一索引——这样不仅能避免重复插入,还能加快查询和更新的速度。


内容的提问来源于stack exchange,提问作者John Wick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:03:07