2010-06-18 39 views
4

我想限制用戶在我的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'的COUNT(*)僅鎖定行?

回答

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; 

這實現防止多於一個的用戶從插入,更新或一次刪除的經理的員工所期望的鎖定。

+0

謝謝......你的解釋幫助我理解了鎖本身給了我什麼。但是具有分析函數(count)的這個查詢是否鎖定了它的值構成的所有行? 'count(*)... for update'會鎖定並鎖定符合'where'條件的記錄嗎? – 2010-06-18 15:43:50

+0

您的查詢將只鎖定在WHERE子句中指定的行。 – 2010-06-21 08:56:10

1

你運行的是哪個數據庫?在DB2中,您可以通過附加'WITH [locking-level]'來控制鎖定行爲,而鎖定級別是4個預定義鎖定級別之一。但是,一般情況下,我不會認爲數據庫會按照您縮進的方式鎖定事物 - 還有鎖升級等事情。如果您想阻止將任何數據插入到表中,那麼可以在DB2中執行'LOCK TABLE table IN EXCLUSIVE MODE'。

+0

我正在使用Oracle 10g。我想避免鎖定所有內容 - 我只是想阻止人們插入/更新/刪除'Red'記錄,而我正在插入/更新/刪除一個記錄。 – 2010-06-18 15:22:03

0

您可以使用Oracle CONTEXT來存儲試圖在FOO表中插入/更新/刪除的Oracle用戶的名稱。當Oracle用戶提交或回滾時,通過使用我的存儲過程USER_LOCK和USER_UNLOCK清空(手動)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; 
/
3

鎖定現有行無法阻止其他會話插入新行。

一種可能的方法是有一個列出可能顏色的COLORS表格。 (您的FOO.COLOR可以擁有COLORS.COLOR的外鍵引用。)然後在插入和更新之前,在COLORS中鎖定相應的行。這將序列化處理相同顏色的所有訪問。