Disons que j'ai une table tbl avec des colonnes id et title. Je dois changer toutes les valeurs de la colonne de titre:
En ce moment, j'effectue deux instructions UPDATE:
UPDATE tbl SET title='a1' WHERE title IN ('a-1', 'a.1')
UPDATE tbl SET title='b1' WHERE title IN ('b-1', 'b.1')
Ce n'est pas du tout un problème, si la table est petite et que l'instruction unique se termine en moins d'une seconde et que vous n'avez besoin que de quelques instructions pour exécuter.
Vous l'avez probablement invité - j'ai une énorme table à traiter (une déclaration se termine en environ 90 secondes), et j'ai un grand nombre de mises à jour à effectuer.
Alors, est-il possible de fusionner les mises à jour afin de ne scanner la table qu'une seule fois? Ou peut-être qu'il y a une meilleure façon de faire face à une situation comme celle-ci.
EDIT: Notez que les vraies données avec lesquelles je travaille et les modifications apportées aux données que je dois effectuer ne sont pas vraiment aussi simples - les chaînes sont plus longues et ne suivent aucun modèle (ce sont des données utilisateur, donc pas d'hypothèses peut être fait - cela peut être n'importe quoi).
Dans un cas plus général, où il peut y avoir plusieurs centaines de mappages à chacune des nouvelles valeurs, vous créez un tableau séparé des anciennes et des nouvelles valeurs, puis vous l'utilisez dans l'instruction UPDATE. Dans un dialecte de SQL:
CREATE TEMP TABLE mapper (old_val CHAR(5) NOT NULL, new_val CHAR(5) NOT NULL);
...multiple inserts into mapper...
INSERT INTO mapper(old_val, new_val) VALUES('a.1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('a-1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('b.1', 'b1');
INSERT INTO mapper(old_val, new_val) VALUES('b-1', 'b1');
...etcetera...
UPDATE tbl
SET title = (SELECT new_val FROM mapper WHERE old_val = tbl.title)
WHERE title IN (SELECT old_val FROM mapper);
Les deux déclarations choisies sont cruciales. La première est une sous-requête corrélée (pas nécessairement rapide, mais plus rapide que la plupart des alternatives si la table de mappage a des milliers de lignes) qui extrait la nouvelle valeur de la table de mappage qui correspond à l'ancienne valeur. La seconde garantit que seules les lignes qui ont une valeur dans la table de mappage sont modifiées; ceci est crucial car sinon, le titre sera défini sur null pour ces lignes sans entrée de mappage (et ce sont les enregistrements qui étaient OK avant de commencer).
Pour quelques alternatives, les opérations CASE sont OK. Mais si vous avez des centaines, des milliers ou des millions de mappages à effectuer, vous risquez de dépasser les limites de la longueur de l'instruction SQL dans votre SGBD.
Vous pouvez utiliser une instruction et un certain nombre d'instructions de cas
update tbl
set title =
case
when title in ('a-1', 'a.1') then 'a1'
when title in ('b-1', 'b.1') then 'b1'
else title
end
Bien sûr, cela entraînera une écriture sur chaque enregistrement, et avec les index, cela peut être un problème, vous pouvez donc filtrer uniquement les lignes que vous souhaitez modifier:
update tbl
set title =
case
when title in ('a-1', 'a.1') then 'a1'
when title in ('b-1', 'b.1') then 'b1'
else title
end
where
title in ('a.1', 'b.1', 'a-1', 'b-1')
Cela réduira le nombre d'écritures sur la table.
En travaillant sur la réponse de Jonathan.
UPDATE tbl
SET title = new_val
FROM mapper
WHERE title IN (SELECT old_val FROM mapper)
AND mapper.old_val = tbl.title;
Sa version initiale nécessiterait un grand nombre de lectures de la table de mappage.
Si les transformations sont aussi simples que vos exemples, vous pouvez faire la mise à jour avec un peu de manipulation de chaîne:
UPDATE tbl
SET title = left(title, 1) + right(title, 1)
WHERE title IN ('a-1', 'a.1', 'b-1', 'b.1')
Est-ce que quelque chose comme ça fonctionnerait pour vous?
Ou
Update Table set
title = Replace(Replace(title, '.', ''), '-', '')
Where title Like '[ab][.-]1'