如果光标只返回一个计数(*)行,我可以锁定游标中的行吗?

时间:2010-06-18 15:14:03

标签: sql oracle plsql locking

我想限制用户在我的FOO表中插入带有color = 'Red'的3条以上的记录。我的意图是A)检索当前计数,以便我可以确定是否允许另一条记录; B)阻止任何其他进程在此进程中插入任何红色记录,因此for update of

我想做点什么:

cursor cur_cnt is
select count(*) cnt from foo
where foo.color = 'Red'
for update of foo.id;

这是否满足我的要求,或者它不会仅锁定具有foo.color = 'Red'的计数(*)中的行?

4 个答案:

答案 0 :(得分:4)

这只会阻止用户更新所选行,而不是添加新行。可靠地实施此类规则的唯一方法是通过检查约束(在“主”表上)和更新主表的“foo”表上的触发器的组合。这样的事情(使用EMP和DEPT熟悉):

alter table dept add (manager_count integer default 0 not null,
   constraint manager_count_chk check (manager_count <= 3));

create trigger emp_trg
before insert or update or delete on emp
for each row
begin
    if inserting or updating then
        if :new.job = 'MANAGER' then
            update dept
            set    manager_count = manager_count+1
            where  deptno = :new.deptno;
        end if;
    end if;
    if updating or deleting then
        if :old.job = 'MANAGER' then
            update dept
            set    manager_count = manager_count-1
            where  deptno = :new.deptno;
        end if;
    end if;
end;

这可以防止多个用户一次插入,更新或删除“MANAGER”员工,从而实现所需的锁定。

答案 1 :(得分:3)

锁定现有行无法阻止其他会话插入新行。

一种可能的方法是使用COLORS表列出可能的颜色。 (然后你的FOO.COLOR可能有一个COLORS.COLOR的外键引用。)然后在进行插入和更新之前锁定COLORS中的相应行。这将序列化处理相同颜色的所有访问。

答案 2 :(得分:1)

你在运行什么数据库?例如,在DB2中,您可以通过附加'WITH [锁定级别]来稍微控制锁定行为,而锁定级别是4个预定义锁定级别之一。一般情况下,我不会假设数据库会完全按照您缩进的方式锁定事物 - 还有锁升级这样的事情。如果要防止将任何数据插入到表中,再次在DB2中,可以执行“LOCK TABLE表IN EXCLUSIVE MODE”。

答案 3 :(得分:0)

您可以使用Oracle CONTEXT来存储试图在FOO表中插入/更新/删除的Oracle用户的NAME。通过使用我的存储过程USER_LOCK和USER_UNLOCK,在Oracle用户提交或回滚时清空(手动)Oracle CONTEXT。通过这种方式,您可以避免同时从多个Oracle用户进行查看/更新和删除,因为您一次授予对一个Oracle用户的访问权限。

使用USER_LOCK(name_of_the_user)过程,您可以在Oracle上下文中添加Oracle用户的名称。使用USER_UNLOCK(name_of_the_user)过程,您可以在Oracle上下文中删除Oracle用户的名称。使用视图locked_users,您可以确定Oracle用户是否被锁定,因为如果它被锁定,其名称将显示在视图中。以下代码创建了所有Oracle结构,以实现所有这些:

CREATE OR REPLACE PACKAGE my_pkg
IS
   PROCEDURE set_session_id (p_session_id NUMBER);

   PROCEDURE set_ctx (p_name VARCHAR2, p_value VARCHAR2);

   PROCEDURE close_session (p_session_id NUMBER);
END;
/

CREATE OR REPLACE PACKAGE BODY my_pkg
IS
   g_session_id   NUMBER;

   PROCEDURE set_session_id (p_session_id NUMBER)
   IS
   BEGIN
      g_session_id := p_session_id;
      DBMS_SESSION.set_identifier (p_session_id);
   END set_session_id;

   --===============================================
   PROCEDURE set_ctx (p_name VARCHAR2, p_value VARCHAR2)
   IS
   BEGIN
      DBMS_SESSION.set_context ('App_Ctx',
                                p_name,
                                p_value,
                                USER,
                                g_session_id);
   END set_ctx;

   --===============================================
   PROCEDURE close_session (p_session_id NUMBER)
   IS
   BEGIN
      DBMS_SESSION.set_identifier (p_session_id);
      DBMS_SESSION.clear_identifier;
   END close_session;
--===============================================
END;
/

    CREATE OR REPLACE CONTEXT APP_CTX
     USING MY_PKG
     ACCESSED GLOBALLY
/


CREATE OR REPLACE TYPE test_type AS TABLE OF VARCHAR2 (30);
/



CREATE OR REPLACE FUNCTION f_convert2 (p_list IN VARCHAR2)
   RETURN test_type
   PIPELINED
AS
   --l_string        LONG        := p_list || ',';
   l_string        VARCHAR2 (4000) := p_list || ',';
   l_comma_index   PLS_INTEGER;
   l_index         PLS_INTEGER := 1;
BEGIN
   LOOP
      l_comma_index := INSTR (l_string, ',', l_index);
      EXIT WHEN l_comma_index = 0;
      PIPE ROW (SUBSTR (l_string, l_index, l_comma_index - l_index));
      l_index := l_comma_index + 1;
   END LOOP;

   RETURN;
END f_convert2;
/



CREATE OR REPLACE FORCE VIEW locked_users (utente)
AS
     SELECT COLUMN_VALUE utente
       FROM TABLE (
               f_convert2 (
                  REPLACE (
                     LTRIM (RTRIM (SYS_CONTEXT ('app_ctx', 'Var1', 4000), '*'),
                            '*'),
                     '**',
                     ',')))
   ORDER BY 1 ASC
/



CREATE OR REPLACE PROCEDURE user_lock (ne_user IN VARCHAR2)
IS
BEGIN
   DECLARE
      indice                NUMBER;
      appoggio_variabile1   VARCHAR2 (250);
   BEGIN
      -- my_pkg.close_session(1234);
      my_pkg.set_session_id (1234);
      appoggio_variabile1 := SYS_CONTEXT ('app_ctx', 'var1');
      DBMS_OUTPUT.put_line (appoggio_variabile1);

      IF INSTR (appoggio_variabile1, ne_user) >= 1
      THEN
         BEGIN
            DBMS_OUTPUT.
             put_line ('The user ' || ne_user || ' is already locked!');
         END;
      ELSE
         BEGIN
            my_pkg.
             set_ctx ('Var1', appoggio_variabile1 || '*' || ne_user || '*');
            DBMS_OUTPUT.
             put_line ('The user ' || ne_user || ' is now locked.');
         END;
      END IF;
   END;
END user_lock;
/



CREATE OR REPLACE PROCEDURE user_unlock (ne_user IN VARCHAR2)
IS
BEGIN
   DECLARE
      indice                NUMBER;
      appoggio_variabile1   VARCHAR2 (250);
   BEGIN
      -- my_pkg.close_session(1234);
      my_pkg.set_session_id (1234);
      appoggio_variabile1 := SYS_CONTEXT ('app_ctx', 'var1');
      DBMS_OUTPUT.put_line (appoggio_variabile1);

      IF INSTR (appoggio_variabile1, ne_user) = 0
         OR appoggio_variabile1 IS NULL
      THEN
         BEGIN
            DBMS_OUTPUT.
             put_line ('The user ' || ne_user || ' is already unlocked!');
         END;
      ELSE
         BEGIN
            my_pkg.
             set_ctx ('Var1',
                      REPLACE (appoggio_variabile1, '*' || ne_user || '*'));
            DBMS_OUTPUT.
             put_line ('The user ' || ne_user || ' is now unlocked.');
         END;
      END IF;
   END;
END user_unlock;
/