sql数据库保证查询结果的一致性

原因

在现有设计下,对业务数据进行查询时可能会碰到关联数据发生变化导致查询结果不一致的情况,例如:
某一商品完成税率调整后,如果相关业务数据没有记录历史数据的税率,就会导致当前查询结果产生的进项税或者销项税税额和调整前查询结果的税额不一致。
这一现象会影响到所有使用外键的情况,大部分情况下对业务影响不大,但涉及税率和考核的情况下就会产生较大影响。特别是在数据库范式设计影响下的数据库设计,影响错综复杂。

解决方案

为保证数据查询结果不受外键变化的影响,这里提供4种解决方案,具体说明如下:

1、通知法

当数据内容发生变更时,将更新内容以传输链的方式向使用该记录作为外键的记录发出通知信息,要求相关记录进行同步变更(处理完当前时间点的所有后续流程),保证数据的一致性。

2、冗余法

使用冗余字段,在数据内容变更可能产生重大影响的情况下,对相关流程数据添加冗余字段,记录业务发生时外键的关键内容。还是以税率为例,在所有涉及结算的数据记录添加税率属性或者税额。
此方法在不改变当前业务架构的情况通过对关键数据表进行调整就可以保证查询结果的一致性,但同样有以下不足:

  • 需要具有预见性
  • 需要在需求发生之前进行调整
  • 对涉及关键数据表的所有过程和报表都得最相应的调整
  • 冗余字段增加存储的消耗量
  • 冗余字段增加内存的消耗量
  • 冗余字段可能影响效率

3、时间节点法

重新对基础数据进行设计(需要调整主键、索引、触发器、存储过程、相关程序等),保证数据内容发生变更时记录当前数据每次变更时数据的有效时间区间和可用标志。此方法可以避免对业务流程的影响,但是要查询历史数据是需要根据业务发生时间关联外键的时间区间进行查询,影响查询效率。

4、组合键对法

在数据库设计阶段考虑基础数据内容变化对业务的影响,通过对基础数据变更的控制和增加额外属性(版本号)达到减小消耗和对效率的影响,达到保证查询结果的一致性。该方法仅适用于新设计的系统开发工作。

可以标志
ID+版本号
添加版本号
基础数据
业务数据
查询结构一致性
修改更新方式
添加外键对

示例:

  • 业务类别表表结构:
-- Create table
create table JCXX_YWLB
(
  bh   NUMBER,
  mc   VARCHAR2(100),
  bz   VARCHAR2(500),
  bbh  NUMBER,
  zt   NUMBER,
  jmsj DATE,
  xgsj DATE
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
-- Add comments to the table 
comment on table JCXX_YWLB
  is '基础信息-业务类别';
-- Add comments to the columns 
comment on column JCXX_YWLB.bh
  is '编号';
comment on column JCXX_YWLB.mc
  is '名称';
comment on column JCXX_YWLB.bz
  is '备注';
comment on column JCXX_YWLB.bbh
  is '版本号';
comment on column JCXX_YWLB.zt
  is '状态';
comment on column JCXX_YWLB.jmsj
  is '建目时间';
comment on column JCXX_YWLB.xgsj
  is '修改时间';
-- Create/Recreate primary, unique and foreign key constraints 
alter table JCXX_YWLB
  add constraint UKEY_YWLB_BHBBH unique (BH, BBH)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
alter table JCXX_YWLB
  add constraint UKEY_YWLB_MCBBH unique (MC, BBH)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
  • 业务类别表变更控制:
-- 业务类别:商品所属业务类别,如:教材教辅、一般图书、非图业务、文创业务
  -- 2023年11月28日
  -- 一起种梧桐吧
  Procedure p001_ywlb(in_type  In Varchar2,
                      in_bh    In Number,
                      in_mc    In Varchar2,
                      in_bz    In Varchar2,
                      out_code Out Number,
                      out_text Out Varchar2) Is
    v_bh  Number;
    v_bbh Number;
  Begin
    out_code := 0;
    Case
    -- 根据 in_type类型选择不同的执行分支
    -- A=新增、U=更新、D=逻辑删除
      When upper(Trim(in_type)) = 'A' Then
        -- 新增:业务类别名称不能为空
        If pk00_comm.f001_isnotnull(in_mc) = False Then
          out_code := -1;
          out_text := '名称不能为空值';
          Return;
        End If;
        Select nvl(Max(bh), 0) + 1 Into v_bh From jcxx_ywlb;
        Insert Into jcxx_ywlb
          (bh, mc, bz, bbh, zt, jmsj, xgsj)
        Values
          (v_bh, in_mc, in_bz, 1, 1, Sysdate, Null);
        out_text := '业务类别添加成功';
        Commit;
      When upper(Trim(in_type)) = 'U' Then
        -- 更新:业务类别编号、名称不能为空
        If pk00_comm.f001_isnotnull(in_bh) = False Then
          out_code := -1;
          out_text := '编号不能为空值';
          Return;
        Elsif pk00_comm.f001_isnotnull(in_mc) = False Then
          out_code := -1;
          out_text := '名称不能为空值';
          Return;
        End If;
        Select nvl(Max(bbh), 0) + 1
          Into v_bbh
          From jcxx_ywlb
         Where bh = in_bh;
        Update jcxx_ywlb
           Set zt = 0, xgsj = Sysdate
         Where bh = in_bh
           And zt = 1;
        Insert Into jcxx_ywlb
          (bh, mc, bz, bbh, zt, jmsj, xgsj)
        Values
          (in_bh, in_mc, in_bz, v_bbh, 1, Sysdate, Null);
        out_text := '业务类别更新成功';
        Commit;
      When upper(Trim(in_type)) = 'D' Then
        -- 逻辑删除:业务类别编号不能为空
        If pk00_comm.f001_isnotnull(in_bh) = False Then
          out_code := -1;
          out_text := '编号不能为空值';
          Return;
        End If;
        Update jcxx_ywlb
           Set zt = 0, xgsj = Sysdate
         Where bh = in_bh
           And zt = 1;
        out_text := '业务类别停用成功';
        Commit;
      Else
        out_code := -1;
        out_text := '未知指令类型';
    End Case;
  Exception
    When Others Then
      out_code := Sqlcode;
      out_text := Sqlerrm;
      Rollback;
  End p001_ywlb;
  • 财务分类表表结构:使用业务类别作为外键
-- Create table
create table JCXX_SPFL_CW
(
  bh       NUMBER,
  mc       VARCHAR2(100),
  bz       VARCHAR2(500),
  bbh      NUMBER,
  zt       NUMBER,
  ywlb_bh  NUMBER,
  ywlb_bbh NUMBER,
  jmsj     DATE,
  xgsj     DATE
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
-- Add comments to the table 
comment on table JCXX_SPFL_CW
  is '基础信息-商品分类-财务分类';
-- Create/Recreate primary, unique and foreign key constraints 
alter table JCXX_SPFL_CW
  add constraint UKEY_SPFL_CW_BHBBH unique (BH, BBH)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
alter table JCXX_SPFL_CW
  add constraint UKEY_SPFL_CW_MCBBH unique (MC, BBH)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
  • 财务分类表变更控制:
-- 财务分类:
  -- 2023年11月28日
  -- 一起种梧桐吧
  Procedure p002_spfl_cw(in_type     In Varchar2,
                         in_bh       In Number,
                         in_mc       In Varchar2,
                         in_bz       In Varchar2,
                         in_ywlb_bh  In Number,
                         in_ywlb_bbh In Number,
                         out_code    Out Number,
                         out_text    Out Varchar2) Is
    v_bh  Number;
    v_bbh Number;
  Begin
    out_code := 0;
    Case
    -- 根据 in_type类型选择不同的执行分支
    -- A=新增、U=更新、D=逻辑删除
      When upper(Trim(in_type)) = 'A' Then
        -- 新增:业务分类名称不能为空、上级分类编号和版本号不能为空
        If pk00_comm.f001_isnotnull(in_mc) = False Then
          out_code := -1;
          out_text := '名称不能为空值';
          Return;
        End If;
        If pk00_comm.f001_isnotnull(in_ywlb_bh) = False Then
          out_code := -1;
          out_text := '业务类别编号不能为空值';
          Return;
        End If;
        If pk00_comm.f001_isnotnull(in_ywlb_bbh) = False Then
          out_code := -1;
          out_text := '业务类别版本号不能为空值';
          Return;
        End If;
        Select nvl(Max(bh), 0) + 1 Into v_bh From jcxx_spfl_cw;
        Insert Into jcxx_spfl_cw
          (bh, mc, bz, bbh, zt, ywlb_bh, ywlb_bbh, jmsj, xgsj)
        Values
          (v_bh,
           in_mc,
           in_bz,
           1,
           1,
           in_ywlb_bh,
           in_ywlb_bbh,
           Sysdate,
           Null);
        out_text := '财务分类添加成功';
        Commit;
      When upper(Trim(in_type)) = 'U' Then
        -- 更新:业务类别编号、名称不能为空
        If pk00_comm.f001_isnotnull(in_bh) = False Then
          out_code := -1;
          out_text := '编号不能为空值';
          Return;
        Elsif pk00_comm.f001_isnotnull(in_mc) = False Then
          out_code := -1;
          out_text := '名称不能为空值';
          Return;
        End If;
        If pk00_comm.f001_isnotnull(in_ywlb_bh) = False Then
          out_code := -1;
          out_text := '业务类别编号不能为空值';
          Return;
        End If;
        If pk00_comm.f001_isnotnull(in_ywlb_bbh) = False Then
          out_code := -1;
          out_text := '业务类别版本号不能为空值';
          Return;
        End If;
        Select nvl(Max(bbh), 0) + 1
          Into v_bbh
          From jcxx_spfl_cw
         Where bh = in_bh;
        Update jcxx_spfl_cw
           Set zt = 0, xgsj = Sysdate
         Where bh = in_bh
           And zt = 1;
        Insert Into jcxx_spfl_cw
          (bh, mc, bz, bbh, zt, ywlb_bh, ywlb_bbh, jmsj, xgsj)
        Values
          (in_bh,
           in_mc,
           in_bz,
           v_bbh,
           1,
           in_ywlb_bh,
           in_ywlb_bbh,
           Sysdate,
           Null);
        out_text := '业务分类更新成功';
        Commit;
      When upper(Trim(in_type)) = 'D' Then
        -- 逻辑删除:业务分类编号不能为空
        If pk00_comm.f001_isnotnull(in_bh) = False Then
          out_code := -1;
          out_text := '编号不能为空值';
          Return;
        End If;
        Update jcxx_spfl_cw
           Set zt = 0, xgsj = Sysdate
         Where bh = in_bh
           And zt = 1;
        out_text := '财务分类停用成功';
        Commit;
      Else
        out_code := -1;
        out_text := '未知指令类型';
    End Case;
  Exception
    When Others Then
      out_code := Sqlcode;
      out_text := Sqlerrm;
      Rollback;
  End p002_spfl_cw;

你可能感兴趣的:(Oracle笔记,数据库,sql,oracle)