Database
 sql >> Base de données >  >> RDS >> Database

5 conseils simples pour utiliser l'instruction SQL UPDATE avec JOIN

"Oups! Ma faute." Combien de fois avez-vous dit cela après qu'une MISE À JOUR SQL ait mal tourné ? Le fait est que si vous ne faites pas attention, une mise à jour de table peut avoir de graves conséquences sous la forme de l'instruction DELETE. Cela pourrait devenir encore pire si vous le compliquez en utilisant UPDATE avec JOIN. C'est pourquoi vous devez y réfléchir avant d'appuyer sur Exécuter ou d'appuyer sur CTRL-E.

Donc, aujourd'hui, vous apprendrez à coder votre mise à jour SQL avec JOIN sans tracas et sans jamais dire "Oups ! Mon mauvais” encore.

Mais avant de nous entraîner, nous commençons par la syntaxe. Cela permettra également à nos débutants de se sentir à l'aise avec SQL Server UPDATE with JOIN. Ensuite, nous préparerons quelques données et quelques exemples. Et enfin, examinez les conseils de sécurité.

[sendpulse-form id="12968″]

SQL UPDATE JOIN Syntaxe

UPDATE table1
SET column1 = <expression1 | value1> [, column2 = <expression2 | value2>, <columnN = expression3 | value3>]
FROM table1
[INNER | OUTER LEFT | OUTER RIGHT] JOIN table2 on table1.keycolumn = table2.keycolumn
[WHERE condition]

Nous devons détailler quelques points à partir de cela.

  1. Nous pouvons mettre à jour un tableau à la fois pour au moins 1 colonne ou quelques colonnes.
  2. Nous avons besoin de la clause FROM pour ajouter un JOIN. L'objet dans la clause FROM peut ou non être le même que l'objet mis à jour.
  3. Nous pouvons utiliser INNER ou OUTER JOIN (voir les exemples plus loin).
  4. Nous ne pouvons mettre à jour qu'un sous-ensemble des données à l'aide de la clause WHERE.

Avant d'avoir nos exemples, préparons les données.

Nos données de test

Pour l'amour des films, créons une base de données de titres de films avec les notes des utilisateurs.

CREATE DATABASE [Movies]
GO

USE [Movies]
GO

CREATE TABLE [dbo].[Titles](
	[TitleID] [int] IDENTITY(1,1) NOT NULL,
	[Title] [varchar](50) NOT NULL,
	[ReleaseDate] [date] NOT NULL,
	[OverallUserRating] [varchar](10) NULL,
 CONSTRAINT [PK_Titles] PRIMARY KEY CLUSTERED 
(
	[TitleID] ASC
))
GO

CREATE TABLE [dbo].[UserRatings](
	[UserRatingID] [int] IDENTITY(1,1) NOT NULL,
	[TitleID] [int] NOT NULL,
	[User] [varchar](50) NOT NULL,
	[Rating] [tinyint] NOT NULL,
 CONSTRAINT [PK_UserRatings] PRIMARY KEY CLUSTERED 
(
	[UserRatingID] ASC
))
GO

ALTER TABLE [dbo].[UserRatings]  WITH CHECK ADD  CONSTRAINT [FK_UserRatings_Titles] FOREIGN KEY([TitleID])
REFERENCES [dbo].[Titles] ([TitleID])
GO

ALTER TABLE [dbo].[UserRatings] CHECK CONSTRAINT [FK_UserRatings_Titles]
GO

ALTER TABLE [dbo].[UserRatings]  WITH CHECK ADD  CONSTRAINT [CK_UserRatings_Rating] CHECK  (([Rating]>=(1) AND [Rating]<=(5)))
GO

ALTER TABLE [dbo].[UserRatings] CHECK CONSTRAINT [CK_UserRatings_Rating]
GO

Maintenant que nous avons la base de données et les tables, insérons quelques données :

INSERT INTO Titles
(Title, ReleaseDate)
VALUES 
('The Avengers', '05/04/2012'),
('Avengers: Age of Ultron','5/1/2015'),
('Avengers: Infinity War','4/27/2018'),
('Avengers: Endgame','4/26/2019'),
('Captain America: Civil War','5/6/2016')
GO

INSERT INTO UserRatings(TitleID, [User], Rating) 
VALUES 
(1,'Natasha',5),
(1,'Bruce',3),
(1,'Tony',4),
(1,'Bucky',5),
(2,'Steve',4),
(2,'Wanda',3),
(2,'Pietro',2),
(2,'Clint',5),
(3,'Hope',5),
(3,'Sam',5),
(3,'Nick',3),
(3,'James',5),
(4,'Scott',5),
(4,'Wong',5),
(4,'Peter',5),
(4,'Carol',4),
(4,'Shuri',5)
GO

MISE À JOUR SQL Server avec JOIGNEZ-VOUS Exemple

Nous examinerons différents exemples ayant le même objectif de mettre à jour le OverallUserRating dans les Titres table. Les notes peuvent aller de 1 à 5. OverallUserRating est la moyenne de toutes les notes d'un titre de film.

Voici l'état initial de la table :

MISE À JOUR LEFT JOIN Exemple

-- SQL UPDATE with LEFT OUTER JOIN
SELECT
 a.TitleID
,CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)) AS AverageRating
INTO #ComputedRatings
FROM titles a
INNER JOIN UserRatings b ON a.TitleID = b.TitleID
GROUP BY a.TitleID

-- mark 'No Rating' if there are no existing ratings
UPDATE Titles
SET OverallUserRating = ISNULL(b.AverageRating,'No Rating') 
FROM Titles a
LEFT JOIN #ComputedRatings b ON a.TitleID = b.TitleID

La première instruction SELECT calcule la note moyenne par titre de film en fonction des UserRatings table. Le résultat est vidé dans une table temporaire appelée #ComputedRatings . Comme nous utilisons INNER JOIN, les titres de films sans classement sont supprimés.

Dans l'instruction UPDATE, les Titres la table a été mise à jour à l'aide d'un LEFT JOIN de #ComputedRatings tableau provisoire. Si la note moyenne est nulle , la valeur devient Pas d'évaluation . Dans notre exemple, Captain America :Civil War n'a pas encore d'évaluations d'utilisateurs - voir Figure 2.

Exemple SQL UPDATE INNER JOIN

Voici comment cela se passe :

-- SQL UPDATE with INNER JOIN
SELECT
 a.TitleID
,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
INTO #ComputedRatings
FROM titles a
LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
GROUP BY a.TitleID


UPDATE Titles
SET OverallUserRating = b.AverageRating
FROM Titles a
INNER JOIN #ComputedRatings b ON a.TitleID = b.TitleID

Lorsque vous exécutez le code ci-dessus, le résultat sera le même que dans la figure 2. Mais quelle est la différence entre les deux codes ?

  • La première instruction SELECT prend en compte la notation utilisateur NULL contrairement à notre exemple LEFT JOIN précédent. Il ne supprime pas le titre du film sans évaluation des utilisateurs. Donc, cette fois, le No Rating valeur pour Captain America :Guerre civile est déjà pris en compte.
  • Un INNER JOIN avec l'attribution directe du AverageRating la valeur est plus appropriée puisque tous les TitleIDs sont comptabilisés.

Désormais, au lieu d'une table temporaire, une expression de table commune (CTE) peut également être utilisée. Voici le code modifié :

-- SQL UPDATE with JOIN using INNER JOIN from a CTE
;WITH ComputedRatings AS
(SELECT
 a.TitleID
,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
FROM Titles a
LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
GROUP BY a.TitleID)
UPDATE Titles
SET OverallUserRating = b.AverageRating
FROM Titles t
INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

Plus d'informations

  • Votre guide ultime de SQL JOIN :INNER JOIN – Partie 1
  • Votre guide ultime de SQL JOIN :OUTER JOIN – Partie 2

Utiliser la mise à jour Commande avec Rejoindre En toute sécurité (5 conseils)

La sécurité fait référence à la mise à jour des enregistrements prévus. De plus, il s'agit de NE PAS toucher aux enregistrements que nous n'avons pas l'intention de mettre à jour. Ici, nous allons traiter 5 scénarios :

Afficher d'abord les enregistrements avec l'instruction SELECT

Cette astuce est parfaite pour quelques enregistrements. Donc, avant d'affecter les enregistrements pour la mise à jour, essayez ceci :

C'est facile à faire. Et si cela semble bon, décommentez les clauses UPDATE et SET. Marquez la clause SELECT comme un commentaire. Alors, vous êtes prêt à partir. De cette façon, vous minimisez le contrôle des dommages en étant proactif.

Effectuer une exécution d'essai à l'aide de tables temporaires

Vous ne savez pas si une erreur peut se produire ? Ensuite, essayez de vider les enregistrements de la table que vous souhaitez mettre à jour dans une table temporaire. Après cela, faites un essai à partir de là.

Il n'y a pas d'erreurs d'exécution ? Les résultats sont-ils bons ? Ensuite, remplacez la table temporaire par la table d'origine. De cette façon, vous savez qu'il n'y aura pas d'erreurs d'exécution en cours de route.

Notez également que les tables temporaires sont l'un des moyens de stocker les copies des tables d'origine. Vous pouvez également utiliser des tables à mémoire optimisée ou une sauvegarde de base de données. Avec les sauvegardes de base de données, vous avez plus de liberté pour jouer avec les enregistrements que vous devez mettre à jour. Mais l'inconvénient est l'espace de stockage.

Plus d'informations

  • CREATE TABLE (Transact-SQL) – Tables temporaires

Essayez d'ajouter la clause OUTPUT dans UPDATE

Voulez-vous remonter le temps avant d'exécuter la mise à jour ? Si vous êtes si sceptique, vous pouvez utiliser la clause OUTPUT et afficher le passé et le présent. Dans l'exemple ci-dessous, une variable de table sert à vider les valeurs précédentes et présentes après la mise à jour. Vous pouvez ensuite SÉLECTIONNER la variable du tableau pour voir les résultats :

DECLARE @tvTitles AS TABLE(TitleID INT NOT NULL,
                           OldOverallRatings VARCHAR(10) NULL,
			    NewOverAllRatings varchar(10) NOT NULL)

;WITH ComputedRatings AS
(
	SELECT
	a.TitleID
	,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
	FROM titles a
	LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
	GROUP BY a.TitleID
)
UPDATE #tmpTitles
SET OverallUserRating = cr.AverageRating
OUTPUT INSERTED.TitleID, DELETED.OverallUserRating, INSERTED.OverallUserRating
INTO @tvTitles
FROM #tmpTitles t
INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

Quelques points à noter à propos de ce code :

  • La variable de table fonctionne comme un conteneur des valeurs précédentes et actuelles.
  • La mise à jour habituelle avec CTE est en ordre. Nous utilisons toujours la table temporaire pour jouer en toute sécurité.
  • La clause OUTPUT s'applique pour vider les valeurs précédentes et présentes dans la variable de table. INSERTED contient de nouvelles valeurs, tandis que DELETED contient d'anciennes valeurs.

Si vous émettez un SELECT à partir de la variable de table, voici ce à quoi vous pouvez vous attendre :

Le résultat ressemble à la figure 3, mais il regarde vers l'avenir. Celui-ci plonge dans le passé. Si c'est différent, quelque chose s'est mal passé entre-temps.

La bonne nouvelle est que vous pouvez utiliser les anciennes valeurs de la variable de table pour la restaurer à son état précédent. Mais si c'est la même chose, là encore, vous êtes prêt à partir. Vous pouvez marquer la clause OUTPUT comme commentaire ou la supprimer, puis remplacer la table temporaire par la table d'origine.

Plus d'informations

  • Clause OUTPUT (Transact-SQL)

Utilisez TRY…CATCH pour gérer les erreurs futures

Les 3 conseils précédents sont utiles lorsque vous créez puis testez votre code. Mais nous savons tous que nous ne pouvons pas tout anticiper. Ainsi, nous devons ajouter plus de filets de sécurité au code.

En parlant de filets de sécurité, T-SQL possède les blocs de gestion des erreurs TRY…CATCH comme C# et C++.

Examinons le code modifié de l'exemple précédent :

BEGIN TRY						  
  ;WITH ComputedRatings AS
  (
    SELECT
     a.TitleID
    ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(20)),
            'No User Ratings Yet') AS AverageRating
    FROM titles a
    LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
    GROUP BY a.TitleID
  )
  UPDATE Titles
  SET OverallUserRating = cr.AverageRating
  FROM Titles t
  INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

END TRY
BEGIN CATCH
 SELECT  
  ERROR_NUMBER() AS ErrorNumber  
 ,ERROR_SEVERITY() AS ErrorSeverity  
 ,ERROR_STATE() AS ErrorState  
 ,ERROR_PROCEDURE() AS ErrorProcedure  
 ,ERROR_LINE() AS ErrorLine  
 ,ERROR_MESSAGE() AS ErrorMessage;  

END CATCH

J'ai changé le code ci-dessus pour forcer l'erreur de troncature de chaîne. La évaluation globale des utilisateurs colonne dans les Titres le tableau peut contenir jusqu'à 10 caractères seulement. Dans le CTE, nous l'avons changé en 20 caractères. Cela ne conviendra pas. Le bloc CATCH prendra en charge le moment de l'occurrence de l'erreur et fournira les informations sur l'erreur.

Voici le résultat :

Nous avons déclenché l'erreur. Si vous avez besoin de détecter des erreurs imprévues pendant l'exécution, c'est une façon de le gérer.

Plus d'informations

  • ESSAYEZ… CATCH (Transact-SQL)

Utiliser la gestion des transactions

Enfin, les opérations. Il garantit de tout restaurer à son état précédent avant que l'erreur ne se produise, y compris UPDATE avec JOIN et toute autre instruction DML. Il s'agit d'un bon ajout au conseil n° 4 ci-dessus.

Modifions à nouveau le code pour inclure les transactions :

BEGIN TRANSACTION

BEGIN TRY						  
  ;WITH ComputedRatings AS
  (
    SELECT
     a.TitleID
    ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(20)),
            'No User Ratings Yet') AS AverageRating
    FROM titles a
    LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
    GROUP BY a.TitleID
  )
  UPDATE Titles
  SET OverallUserRating = cr.AverageRating
  FROM Titles t
  INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

  COMMIT TRANSACTION
END TRY
BEGIN CATCH
 SELECT  
  ERROR_NUMBER() AS ErrorNumber  
 ,ERROR_SEVERITY() AS ErrorSeverity  
 ,ERROR_STATE() AS ErrorState  
 ,ERROR_PROCEDURE() AS ErrorProcedure  
 ,ERROR_LINE() AS ErrorLine  
 ,ERROR_MESSAGE() AS ErrorMessage;  

 ROLLBACK TRANSACTION
END CATCH

C'est la même chose que dans l'exemple précédent sauf pour les transactions. Ainsi, cela forcera une erreur de troncature de chaîne. Il ne dépassera pas COMMIT TRANSACTION, mais plutôt dans le bloc CATCH avec ROLLBACK TRANSACTION pour rétablir les valeurs à leurs états précédents.

C'est ainsi que nous voulons jouer en toute sécurité avec les mises à jour, les insertions et les suppressions.

Remarque  :Vous pouvez concevoir n'importe quelle requête visuellement dans un diagramme à l'aide de la fonction de générateur de requêtes de dbForge Studio pour SQL Server.

Plus d'informations

  • Bonnes pratiques T-SQL
  • Comment écrire des requêtes T-SQL comme un pro

Conclusion

Vous avez vu la syntaxe de SQL UPDATE avec JOIN. Des exemples et 5 conseils simples vous ont éclairé davantage. Les exigences peuvent différer de ce que présentent les exemples, mais vous avez compris. Vous pouvez encore faire des erreurs. Cependant, il est possible de les réduire à près de zéro.

Pourquoi ne pas appliquer ces idées à votre situation ?

Si cet article vous a été utile, n'hésitez pas à passer le mot sur vos plateformes de médias sociaux préférées. Et si vous souhaitez ajouter de bonnes idées, vous êtes les bienvenus dans la section Commentaires.