Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql ";“实体”;特定序列_Sql_Oracle - Fatal编程技术网

Sql ";“实体”;特定序列

Sql ";“实体”;特定序列,sql,oracle,Sql,Oracle,背景 我有很多不同的“事物”(特定于领域的项目/实体/主题),它们对“事物”所有者(人类)是可见的。业主们将用数字来识别他们的“东西”。而不是显示一个大的“随机”数字,我想给他们显示一个小的数字(最好是从1开始的序列),这对人类来说更容易。老板们很乐意谈论“我的富37”和“她的酒吧128”。“序列”可以有间隙,但附加的编号必须在“对象”的生命周期内保持不变。所以我需要一种方法来生成“thing”+特定于所有者的id(目前称为“visible id”) “物”+所有者组合的数量为10k+。目前,新

背景

我有很多不同的“事物”(特定于领域的项目/实体/主题),它们对“事物”所有者(人类)是可见的。业主们将用数字来识别他们的“东西”。而不是显示一个大的“随机”数字,我想给他们显示一个小的数字(最好是从1开始的序列),这对人类来说更容易。老板们很乐意谈论“我的富37”和“她的酒吧128”。“序列”可以有间隙,但附加的编号必须在“对象”的生命周期内保持不变。所以我需要一种方法来生成“thing”+特定于所有者的id(目前称为“visible id”)

“物”+所有者组合的数量为10k+。目前,新的“东西”无法动态生成,但所有者可以

每个所有者拥有一个“thing”实例的数量相对较少,大约为数十个,但是没有可以从业务规则中派生的硬上限。新的“东西”实例经常被创建和删除

考虑过的选项

我发现一个非常好的讨论是在一个非常好的问题中进行的,它解决了与我几乎相同的问题

到目前为止,我已经考虑了以下选择:

  • 我认为一个标准的数据库序列是非常好的,但这需要我动态地创建大量“thing”+所有者特定的序列,并在插入过程中解析序列名。(并在所有者不在时删除序列。)我不确定创建大量序列是否是一种好的做法(对我来说,10k+数据库对象是一个巨大的数字)
  • 我也考虑过臭名昭著的
    max(visible_id)+1
    ,但是我们会遇到正常的并发问题,所以这是不可能的
  • 不要将特定于所有者的id保存到数据库中,而是在select like by中生成它。这是一个很好的想法,但不幸的是,id在“thing”实例生命周期中必须相同
  • 通过让所有者命名“东西”来避免整个问题。但他们一点也不喜欢这个想法——“为什么我要麻烦,说foo 16真是太容易了。” 问题

    create table foo (
     id number primary key -- the key for computers
    ,owner_id number
    ,visible_id number -- the key for humans
    ,data_ varchar2(20)
    );
    
    create constraint foo_u1 unique foo(owner_id, visible_id);
    
    -- primary key sequence
    create sequence foo_id_seq;
    
    insert into foo values(
     foo_id_seq.nextval
    ,1
    ,1 -- what to put here ?
    ,'lorem ipsum'
    );
    
    insert into foo values(
     foo_id_seq.nextval
    ,2
    ,1 -- what to put here ?
    ,'dolor sit amet'
    );
    
    select visible_id, data_ from foo where owner = 2 order by visible_id;
    
    是否有其他方法来解决此问题,或者我应该开始动态创建序列?如果序列是答案,请详细说明可能存在哪些陷阱(如DDL中的隐式提交)

    我对Oracle 11gR2和12c解决方案都感兴趣(如果它们不同的话)

    说明问题的伪代码

    create table foo (
     id number primary key -- the key for computers
    ,owner_id number
    ,visible_id number -- the key for humans
    ,data_ varchar2(20)
    );
    
    create constraint foo_u1 unique foo(owner_id, visible_id);
    
    -- primary key sequence
    create sequence foo_id_seq;
    
    insert into foo values(
     foo_id_seq.nextval
    ,1
    ,1 -- what to put here ?
    ,'lorem ipsum'
    );
    
    insert into foo values(
     foo_id_seq.nextval
    ,2
    ,1 -- what to put here ?
    ,'dolor sit amet'
    );
    
    select visible_id, data_ from foo where owner = 2 order by visible_id;
    

    因为间隙是可以的,所以您应该实现“选项2”的变体。允许间隙意味着您可以快速完成同步:竞争会话只需检查并继续,而不必等待其他会话是否提交或回滚

    如果Oracle提供了一个
    插入到..NOWAIT
    选项,这将很容易。事实上,我可能会涉及到
    DBMS\u LOCK
    。下面是我对您的API的看法

    它对你的最大可见ID做了一些假设,因为你在最初的帖子中做了这些假设

    CREATE OR REPLACE PACKAGE foo_api AS
      PROCEDURE create_foo (p_owner_id NUMBER, p_data VARCHAR2);
    END foo_api;
    
    CREATE OR REPLACE PACKAGE BODY foo_api AS
      -- We need to call allocate_unique in an autonomous transaction because
      -- it commits and the calling program may not want to commit at this time
      FUNCTION get_lock_handle (p_owner_id NUMBER, p_visible_id NUMBER)
        RETURN VARCHAR2 IS
        PRAGMA AUTONOMOUS_TRANSACTION;
        l_lock_handle   VARCHAR2 (128);
      BEGIN
        DBMS_LOCK.allocate_unique (
          lockname  => 'INSERT_FOO_' || p_owner_id || '_' || p_visible_id,
          lockhandle => l_lock_handle
        );
        COMMIT;
        RETURN l_lock_handle;
      END;
    
    
      PROCEDURE create_foo (p_owner_id NUMBER, p_data VARCHAR2) IS
        -- This is the highest visible ID you'd ever want.
        c_max_visible_id   NUMBER := 1000;
      BEGIN
       <<id_loop>>
        FOR r_available_ids IN (SELECT a.visible_id
                                FROM   (SELECT ROWNUM visible_id
                                        FROM   DUAL
                                        CONNECT BY ROWNUM <= c_max_visible_id) a
                                       LEFT JOIN foo
                                         ON foo.owner_id = p_owner_id
                                         AND foo.visible_id = a.visible_id
                                WHERE  foo.visible_id IS NULL) LOOP
          -- We found a gap
          -- We could try to insert into it.  If another session has already done so and
          -- committed, we'll get an ORA-00001.  If another session has already done so but not 
          -- yet committed, we'll wait.  And waiting is bad.
          -- We'd like an INSERT...NO WAIT, but Oracle doesn't provide that.
          -- Since this is the official API for creating foos and we have good application 
          -- design to ensure that foos are not created outside this API, we'll manage 
          -- the concurrency ourselves.
          --
          -- Try to acquire a user lock on the key we're going to try an insert.
          DECLARE
            l_lock_handle       VARCHAR2 (128);
            l_lock_result       NUMBER;
            l_seconds_to_wait   NUMBER := 21600;
          BEGIN
            l_lock_handle := get_lock_handle (
              p_owner_id => p_owner_id,
              p_visible_id => r_available_ids.visible_id
            );
    
            l_lock_result := DBMS_LOCK.request (lockhandle => l_lock_handle,
                                                lockmode   => DBMS_LOCK.x_mode,
                                                timeout    => 0, -- Do not wait
                                                release_on_commit => TRUE);
    
            IF l_lock_result = 1 THEN
              -- 1 => Timeout -- this could happen.
              -- In this case, we want to move onto the next available ID.
              CONTINUE id_loop;
            END IF;
    
            IF l_lock_result = 2 THEN
              -- 2 => Deadlock (this should never happen, but scream if it does).
              raise_application_error (
                -20001,
                   'A deadlock occurred while trying to acquire Foo creation lock for '
                || p_owner_id
                || '_'
                || r_available_ids.visible_id
                || '.  This is a programming error.');
            END IF;
    
            IF l_lock_result = 3 THEN
              -- 3 => Parameter error (this should never happen, but scream if it does).
              raise_application_error (
                -20001,
                   'A parameter error occurred while trying to acquire Foo creation lock for '
                || p_owner_id
                || '_'
                || r_available_ids.visible_id
                || '.  This is a programming error.');
            END IF;
    
            IF l_lock_result = 4 THEN
              -- 4 => Already own lock (this should never happen, but scream if it does).
              raise_application_error (
                -20001,
                   'Attempted to create a Foo creation lock and found lock already held by session for '
                || p_owner_id
                || '_'
                || r_available_ids.visible_id
                || '.  This is a programming error.');
            END IF;
    
            IF l_lock_result = 5 THEN
              -- 5 => Illegal lock handle (this should never happen, but scream if it does).
              raise_application_error (
                -20001,
                   'An illegal lock handle error occurred while trying to acquire Foo creation lock for '
                || p_owner_id
                || '_'
                || r_available_ids.visible_id
                || '.  This is a programming error.');
            END IF;
          END;
    
          -- If we get here, we have an exclusive lock on the owner_id / visible_id 
          -- combination.  Attempt the insert
          BEGIN
            INSERT INTO foo (id,
                             owner_id,
                             visible_id,
                             data_)
            VALUES (foo_id_seq.NEXTVAL,
                    p_owner_id,
                    r_available_ids.visible_id,
                    p_data);
    
            -- If we get here, we are done.
            EXIT id_loop;
          EXCEPTION
            WHEN DUP_VAL_ON_INDEX THEN
              -- Unfortunately, if this happened, we would have waited until the competing 
              -- session committed or rolled back.  But the only way it
              -- could have happened if the competing session did not use our API to create 
              -- or update the foo.
              -- TODO: Do something to log or alert a programmer that this has happened, 
              -- but don't fail.
              CONTINUE id_loop;
          END;
        END LOOP;
      END create_foo;
    END foo_api;
    
    创建或替换包foo_api作为
    程序创建_foo(p_所有者_id号,p_数据VARCHAR2);
    结束福尤api;
    创建或替换包体foo_api作为
    --我们需要在自治事务中调用allocate_unique,因为
    --它提交,调用程序此时可能不想提交
    函数get\u lock\u handle(p\u所有者\u id号,p\u可见\u id号)
    返回VARCHAR2是
    布拉格自治交易;
    l_锁柄VARCHAR2(128);
    开始
    DBMS_LOCK.allocate_unique(
    lockname=>“插入|FOO|p|u所有者|u id|p|u可见|u id”,
    锁定手柄=>l\U锁定手柄
    );
    犯罪
    返回锁定手柄;
    结束;
    创建\u foo(p\u所有者\u id号,p\u数据VARCHAR2)的过程是
    --这是你想要的最高可见ID。
    c_max_visible_id NUMBER:=1000;
    开始
    对于中的r\u可用\u id(选择一个可见\u id
    从(选择ROWNUM visible\u id
    来自双重
    通过ROWNUM p_owner_id连接,
    p_visible_id=>r_available_id.visible_id
    );
    l\u lock\u result:=DBMS\u lock.request(lockhandle=>l\u lock\u handle,
    lockmode=>DBMS_LOCK.x_模式,
    超时=>0,--不要等待
    发布时发布(提交=>TRUE);
    如果l_lock_result=1,则
    --1=>超时--这可能发生。
    --在本例中,我们希望转到下一个可用ID。
    继续id_循环;
    如果结束;
    如果l_lock_result=2,则
    --2=>死锁(这种情况永远不会发生,但如果发生就尖叫)。
    引发应用程序错误(
    -20001,
    '尝试获取的Foo创建锁时发生死锁'
    ||p_所有者\u id
    || '_'
    ||r\u可用\u id。可见\u id
    ||“。这是一个编程错误。”);
    如果结束;
    如果l_lock_result=3,则
    --3=>参数错误(这种情况永远不会发生,但如果发生会尖叫)。
    引发应用程序错误(
    -20001,
    '尝试获取的Foo创建锁时发生参数错误'
    ||p_所有者\u id
    || '_'
    ||r\u可用\u id。可见\u id
    ||“。这是一个编程错误。”);
    如果结束;
    如果l_lock_result=4,则
    --4=>已经拥有锁(这种情况永远不会发生,但如果发生会尖叫)。
    引发应用程序错误(
    -20001,
    '尝试创建Foo创建锁,但发现会话已为持有该锁'
    ||p_所有者\u id
    || '_'
    ||r\u可用\u ID可见