1
votes

Exécutez deux UPDATE sur des données complémentaires à la fois dans Oracle

J'ai le tableau suivant que je dois mettre à jour:

MERGE INTO USER U
USING (
    -- Subquery that generates a list of values
) T
ON (U.REF_COL = T.VALUE)
WHEN MATCHED THEN
    UPDATE SET U.ACTIVE = 1
WHEN NOT MATCHED THEN
    UPDATE SET U.ACTIVE = 0

J'exécute les deux instructions UPDATE suivantes séparément:

  1. Déclaration-1

    UPDATE
        USER
    SET
        ACTIVE = 0
    WHERE
        REF_COL NOT IN (
            -- Subquery that generates a list of values
        )
    
  2. Déclaration-2

    UPDATE
        USER
    SET
        ACTIVE = 1
    WHERE
        REF_COL IN (
            -- Subquery that generates a list of values
        )
    

La sous-requête qui génère une liste de valeurs est la même pour les deux requêtes UPDATE .

Y a-t-il un moyen qui m'aidera à exécuter la requête immédiatement, comme MERGE?

La requête suivante utilisant MERGE code > l'instruction n'est pas valide:

USER:

| ID | ACTIVE | REF_COL | COL_2 | COL_3 |
|----|--------|---------|-------|-------|
| 1  |    1   |  value  | value | value |
| 2  |    0   |  value  | value | value |
| 3  |    1   |  value  | value | value |

Puisque la clause WHEN NOT MATCHED THEN s'attend à avoir une instruction INSERT . p>


4 commentaires

s'il vous plaît montrer la sous-requête!


@nikhilsugandh, la question que j'ai posée pour démontrer le problème d'une manière simple. Dans le cas réel, il y a beaucoup de tables associées et je ne peux pas exposer leur nom.


la colonne ref_col de la table user peut-elle contenir des valeurs nulles? Si tel est le cas, ces lignes doivent-elles également être mises à jour ou ignorées?


@Boneist, non le REF_COL est défini pour être non nul.


4 Réponses :


2
votes

Utilisez CASE dans SET

UPDATE
    USER U
SET
    U.ACTIVE = (CASE WHEN U.REF_COL IN (<subquery>) THEN 0 ELSE 1 END)

DEMO


4 commentaires

Je soupçonne que la sous-requête serait évaluée pour chaque ligne de USER .


@TapasBose Si la sous-requête renvoie une liste statique de valeurs, elle sera mise en cache, ce qui signifie qu'elle ne sera exécutée qu'une seule fois.


@TapasBose certainement pas dans Oracle


Éviter la fusion est certainement la meilleure solution.



0
votes

pesudocode pour la fusion:

CREATE OR REPLACE PROCEDURE all_updates_in_user
IS
   cursor c1 is<<query that generates a list of values>>
BEGIN

  FOR rec in c1
   LOOP

   UPDATE
    USER
SET
    ACTIVE = 1
WHERE
    REF_COL IN rec.columnname;

   UPDATE
    USER
SET
    ACTIVE = 0
WHERE
    REF_COL  NOT IN rec.columnname;  
 END LOOP;
END;

oracle docs ressemble à: La clause merge_insert_clause spécifie les valeurs à insérer dans la colonne de la table cible si la condition de la clause ON est fausse. Si la clause d'insertion est exécutée, tous les déclencheurs d'insertion définis sur la table cible sont activés. Si vous omettez la liste de colonnes après le mot clé INSERT, le nombre de colonnes de la table cible doit correspondre au nombre de valeurs de la clause VALUES. vous ne pouvez pas utiliser la fusion ou deux mises à jour une pour la correspondance et une autre pour la non-correspondance.

vous devrez créer une procédure à la place.

MERGE into <target table>

    USING
        <souce table/view/result of subquery>
    ON
        <match condition>
    WHEN MATCHED THEN
        <update clause>
        <delete clause>
    WHEN NOT MATCHED THEN
        <insert clause>


0 commentaires

1
votes

Voici une version merge avec exemple. Vous devez placer la logique dans la partie source :

merge into users tgt
using (
  select u.ref_col, nvl2(s.ref_col, 1, 0) active
    from users u 
    left join subquery s on u.ref_col = s.ref_col ) src
on (tgt.ref_col = src.ref_col)
when matched then update set active = src.active;

exemple de dbfiddle retour


1 commentaires

Salut, pourriez-vous s'il vous plaît vérifier la réponse que j'ai donnée. De plus, votre idée a fonctionné comme un charme, donc j'ai accepté la réponse.



1
votes

En utilisant l'idée fournie par Ponder Stibbons dans sa réponse , j'ai créé ce qui suit mettre en doute; la logique supplémentaire que j'ai ajoutée est de filtrer uniquement les lignes pour lesquelles la valeur ACTIVE doit être mise à jour:

MERGE INTO USER U
USING (
    SELECT
        TGT.ID,
        SRC.ACTIVE
    FROM
        USER TGT
        JOIN (
            SELECT
                U.REF_COL,
                NVL2(T.REF_COL, 1, 0) AS ACTIVE
            FROM
                USER U
                LEFT JOIN (
                    -- Subquery that generates a list of values
                ) T ON T.REF_COL = U.REF_COL            
        ) SRC ON TGT.REF_COL = SRC.REF_COL
    WHERE
        TGT.ACTIVE != SRC.ACTIVE
) F
ON (U.ID = F.ID)
WHEN MATCHED THEN
    UPDATE SET
        U.ACTIVE = F.ACTIVE


2 commentaires

Semble bien. Si je comprends bien la ligne WHERE TGT.ACTIVE! = SRC.ACTIVE , c'est parce que vous souhaitez mettre à jour uniquement les lignes qui diffèrent. Si la colonne USER.ACTIVE ne contient pas / ne peut pas contenir de valeurs nulles, cela devrait fonctionner correctement.


Exactement. Le ACTIVE est non nullable. Merci encore.