2010-06-18 12 views
4

Je voudrais limiter les utilisateurs d'insérer plus de 3 enregistrements avec color = 'Red' dans ma table FOO. Mes intentions sont de A) récupérer le compte actuel afin que je puisse déterminer si un autre enregistrement est autorisé et B) empêcher d'autres processus d'insérer des enregistrements rouges pendant que celui-ci est en cours, d'où le for update of.Puis-je verrouiller des lignes dans un curseur si le curseur ne renvoie qu'une seule ligne de comptage (*)?

Je voudrais faire quelque chose comme:

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

Est-ce que ce satisfaire mes deux exigences ou non seulement il verrouiller les lignes dans le nombre (*) qui avait foo.color = 'Red'?

Répondre

4

Cela empêche uniquement les utilisateurs de mettre à jour les lignes sélectionnées, pas d'ajouter de nouvelles. La seule façon d'appliquer de manière fiable une telle règle consiste à combiner une contrainte de vérification (sur une table "maître") et un déclencheur sur la table "foo" qui met à jour la table principale. Quelque chose comme cela (en utilisant EMP et DEPT de familiarité):

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; 

Cela permet d'obtenir le blocage désiré en empêchant plus d'un utilisateur d'insérer, mettre à jour ou supprimer des « manager » employés à la fois.

+0

Merci ... votre explication m'a aidé à comprendre ce que la serrure elle-même me donne. Mais cette requête avec une fonction analytique (count) verrouille-t-elle toutes les lignes à partir desquelles sa valeur est constituée? Le 'compte (*) ... pour la mise à jour de' verrouillera, et verrouillera seulement, les enregistrements qui répondaient aux conditions de la clause 'where'? –

+0

Votre requête verrouillera uniquement les lignes spécifiées dans la clause WHERE. –

1

Quelle base de données utilisez-vous? Dans DB2 par exemple, vous pouvez contrôler le comportement de verrouillage en ajoutant 'WITH [niveau de verrouillage]', tandis que le niveau de verrouillage est l'un des 4 niveaux de verrouillage prédéfinis. En général, cependant, je ne suppose pas que la base de données verrouillera les choses exactement de la même façon que vous indentez - il y a aussi des choses comme l'escalade de verrous. Si vous voulez empêcher toute donnée d'être insérée dans la table, encore une fois dans DB2, vous pouvez faire la 'TABLE LOCK TABLE EN MODE EXCLUSIVE'.

+0

J'utilise Oracle 10g. Je voudrais éviter de tout verrouiller - je veux juste empêcher les gens d'insérer/mettre à jour/supprimer des enregistrements «rouges» pendant que je suis en train d'insérer/mettre à jour/supprimer un déjà. –

0

Vous pouvez utiliser un contexte Oracle pour stocker le nom de l'utilisateur Oracle qui tente d'insérer/mettre à jour/supprimer dans le tableau FOO. Vous videz (manuellement) le CONTEXT Oracle lorsque l'utilisateur Oracle valide ou annule, en utilisant mes procédures stockées USER_LOCK et USER_UNLOCK. De cette façon, vous pouvez éviter les insertions/mises à jour et suppressions simultanées de plusieurs utilisateurs Oracle, car vous accordez l'accès à un utilisateur Oracle à la fois.

Avec la procédure USER_LOCK (name_of_the_user) vous pouvez ajouter le nom de l'utilisateur Oracle dans le contexte Oracle. Avec la procédure USER_UNLOCK (nom_utilisateur_utilisateur), vous pouvez supprimer le nom de l'utilisateur Oracle dans le contexte Oracle. En utilisant la vue locked_users, vous pouvez déterminer si un utilisateur Oracle est verrouillé ou non, car s'il est verrouillé, son nom apparaît dans la vue. Le code suivant crée toutes les structures Oracle pour réaliser tout cela:

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

lignes existantes de verrouillage ne peuvent pas empêcher d'autres sessions d'insérer de nouvelles lignes.

Une approche possible est d'avoir une table COULEURS répertoriant les couleurs possibles. (Votre FOO.COLOR pourrait alors avoir une référence de clé étrangère à COLORS.COLOR.) Puis verrouillez la ligne appropriée dans COLORS avant de faire vos insertions et mises à jour. Cela sérialisera tous les accès qui traitent de la même couleur.