2009-01-05 9 views
24

Disons que j'ai un tableau tbl avec les colonnes id et titre. Je dois changer toutes les valeurs de la colonne de titre:Est-il possible d'effectuer plusieurs mises à jour avec une seule instruction SQL UPDATE?

  1. de 'A1' à 'A1',
  2. de 'a.1' à 'A1',
  3. de « b-1 'à' b1 ',
  4. de' b.1 'à' b1 '.

En ce moment, je joue deux 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 la seule déclaration complète en moins d'une seconde et Vous n'avez besoin que de quelques instructions à exécuter.

Vous l'avez probablement invité - j'ai une énorme table à traiter (une déclaration complète en environ 90 secondes), et j'ai un grand nombre de mises à jour à effectuer.

Donc, est-il possible de fusionner les mises à jour pour ne balayer la table qu'une seule fois? Ou peut-être, il y a une meilleure façon de faire face à une telle situation.

EDIT: Notez que les données réelles avec lesquelles je travaille et les modifications que je dois effectuer ne sont pas si simples: les chaînes sont plus longues et ne suivent aucun modèle (ce sont des données utilisateur) , donc aucune hypothèse ne peut être faite - cela peut être n'importe quoi).

+0

Donc, en déduisant de votre commentaire EDIT, les chaînes elles-mêmes peuvent être différentes, mais la mise à jour que vous tentez suit-elle un modèle? Si oui, qu'est-ce que c'est? Si rien n'a un motif, alors il n'y a pas de solution, vous devez coder chaque mise à jour idiosyncrasique individuellement. –

+0

J'ai une liste de valeurs * correctes *, et j'ai une liste clairement spécifiée de * mauvaises * valeurs (et quelle valeur doit être fausse changé à quelle valeur correcte). Donc oui - les mises à jour ont un motif. En bref - chaque mise à jour modifie UNE valeur, mais seulement si l'ancienne valeur est IN dans la liste de valeurs spécifiée. – Paulius

Répondre

19

Dans un cas plus général, où il pourrait y avoir plusieurs centaines de correspondances à chacune des nouvelles valeurs, vous devez créer une table séparée des valeurs anciennes et nouvelles, et ensuite utiliser ce 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 instructions select 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 contient des milliers de lignes) qui extrait la nouvelle valeur de la table de mappage correspondant à 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 autrement, le titre sera défini sur null pour les lignes sans une 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.

+0

Ceci est très très intéressant. Je n'ai jamais pensé à ça. Les insertions dans un mapper seraient toujours rapides, et la mise à jour ne balayerait qu'une fois ma table et je n'aurais pas besoin de construire d'énormes requêtes. – Paulius

+0

mais mieux d'utiliser une jointure qu'une sous-requête corrélée pour des raisons de performances. – HLGEM

+0

@HLGEM: oui, si votre SGBD supporte la notation. Souhaitez-vous proposer une syntaxe de travail pour certains SGBD que vous connaissez? Si oui, s'il vous plaît modifier ma réponse - Je crois que vous avez assez de rep pour le faire. Ou laissez-moi savoir par email - voir ma page de profil. –

3

Si les transformations sont aussi simples que vos exemples, vous pouvez faire la mise à jour avec un peu de manipulation de chaînes:

UPDATE tbl 
SET title = left(title, 1) + right(title, 1) 
WHERE title IN ('a-1', 'a.1', 'b-1', 'b.1') 

Would quelque chose comme ça pour vous?

+0

Non, malheureusement, les vraies données que je traite ne sont pas aussi simples que dans mon exemple. Cela ne marcherait pas pour moi. Merci quand même. – Paulius

+0

On dirait que l'utilisation de CASE WHEN par casperOne est la voie à suivre. –

21

Vous pouvez utiliser une instruction et un certain nombre de déclarations 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 provoquera une écriture sur chaque enregistrement, et avec des index, il peut être un problème, vous pouvez 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 permettra de réduire le nombre d'écritures à la table.

0

Ou

Update Table set 
    title = Replace(Replace(title, '.', ''), '-', '') 
    Where title Like '[ab][.-]1' 
+0

Comme je l'ai mentionné dans les commentaires de la réponse de Matt - les données ne sont pas aussi simples dans la base de données réelle. – Paulius

+0

Au risque de paraître évident, eh bien, la réponse sera aussi moins simple. Quel est le vrai problème? –

+0

Peut-être, j'aurais dû faire les titres différents dans l'exemple, de sorte qu'ils n'étaient pas aussi simples. Le vrai problème est que les vrais titres sont des chaînes qui ne suivent aucun modèle - ils sont en fait des titres générés par l'utilisateur, donc je ne peux faire aucune supposition à leur sujet. J'ai également édité ma question. – Paulius

8

Travailler à partir de la réponse de Jonathan. Sa version initiale nécessiterait un grand nombre de lectures dans la table de mappeur.

+0

@MrDenny: puis-je copier votre matériel dans ma réponse - avec crédit, bien sûr? –

+0

J'ai utilisé sa requête, et cela a fonctionné comme un charme! J'étais vraiment surpris - c'était vraiment plus rapide que je ne le pensais. Très agréable. – Paulius