【问题标题】:How to create a unique index for a subset of rows in a db2 table如何为 db2 表中的行子集创建唯一索引
【发布时间】:2019-02-23 09:38:55
【问题描述】:

我正在尝试为特定表中的数据子集创建唯一索引。现有数据是这样的 -

但实际数据应该是这样的——

行子集将是条件状态为 A 或 B 的行。对于这些行集,unique_id 和金额值组合应该是唯一的。

这里使用的 DB2 版本是 Windows 服务器上的 9.7。 DB2 中是否可以使用部分索引或条件索引?

【问题讨论】:

    标签: db2 db2-luw


    【解决方案1】:

    新表

    create or replace function generate_unique_det()
    returns varchar(13) for bit data
    deterministic
    no external action
    contains sql
    return generate_unique();
    
    create table test_unique (
      unique_id int not null
    , status char(1) not null
    , amount int not null
    , status2 varchar(13) for bit data not null generated always as 
      (case when status in ('A', 'B') then '' else generate_unique_det() end)
    ) in userspace1;
    
    create unique index test_unique1 on test_unique (unique_id, amount, status2);
    
    insert into test_unique (unique_id, status, amount) 
    values
      (1234, 'A', 400)
    --, (1234, 'B', 400)
    , (1234, 'Z', 400)
    , (1234, 'Z', 400);
    

    标准的generate_unique 函数不是确定性的。 generated always 子句中不允许使用此类函数。 这就是我们基于标准函数创建自己的函数的原因。
    这种“假”函数的问题可能是,如果 Db2 在多行更改操作期间实际上没有为每个更新/插入的行调用此函数(为什么要在同一组参数上多次调用 deterministic 函数,如果一次就足够了,然后将结果重用于其他受影响的行)。但它在现实中有效 - Db2 确实为每个受影响的行调用了这样一个函数,这在我们的例子中是需要的。
    您无法在最后一条语句中插入注释掉的行。

    现有表

    set integrity for test_unique off;
    
    alter table test_unique add 
    status2 varchar(13) for bit data not null 
    generated always as (case when status in ('A', 'B') then '' else generate_unique_det() end);
    
    set integrity for test_unique immediate checked force generated;
    
    -- If you need to save the rows violated future unique index
    create table test_unique_exc like test_unique in userspace1;
    
    -- If you don't need to save the the rows violated future unique index,  
    -- then just run the inner DELETE statement only,
    -- which just removes these rows.
    -- The whole statement inserts the deleted rows into the "exception table".
    with d as (
      select unique_id, status, amount, status2
      from old table (
        delete from (select t.*, rownumber() over (partition by unique_id, amount, status2) rn_ from test_unique t) where rn_>1
      )
    )
    select count(1)
    from new table (
      insert into test_unique_exc select * from d
    );
    
    create unique index test_unique1 on test_unique (unique_id, amount, status2);
    

    【讨论】:

    • 非常感谢您的回复。如果 test_unique 是一个包含大约 50,000 行的现有表怎么办?我可以通过添加一个新列 status2 来更改现有表,其条件或位数据始终不为空(当状态在 ('A', 'B') then '' else generate_unique_det() end 时的情况)?
    • @Manoj 已添加现有表的解决方案。
    猜你喜欢
    • 2011-07-04
    • 1970-01-01
    • 1970-01-01
    • 2013-01-24
    • 1970-01-01
    • 1970-01-01
    • 2018-01-31
    • 2022-11-29
    • 1970-01-01
    相关资源
    最近更新 更多