Database
 sql >> Database >  >> RDS >> Database

5 semplici suggerimenti per utilizzare l'istruzione SQL UPDATE con JOIN

“Ops! Colpa mia." Quante volte l'hai detto dopo che un AGGIORNAMENTO SQL era andato storto? Il fatto è che, se non stai attento, un aggiornamento della tabella può avere gravi conseguenze sotto forma dell'istruzione DELETE. Potrebbe diventare anche peggio se lo complichi usando UPDATE con JOIN. Ecco perché devi pensarci su prima di premere Esegui o premere CTRL-E.

Quindi, oggi imparerai come codificare il tuo AGGIORNAMENTO SQL con JOIN senza problemi e non dire mai "Oops! Il mio male” di nuovo.

Ma prima di iniziare a esercitarci, iniziamo con la sintassi. Farà anche sentire a casa i nostri neofiti con l'AGGIORNAMENTO di SQL Server con JOIN. Quindi, prepareremo alcuni dati e alcuni esempi. E infine, esamina i suggerimenti per la sicurezza.

[sendpulse-form id=”12968″]

SQL UPDATE JOIN sintassi

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]

Abbiamo bisogno di dettagliare alcuni punti da questo.

  1. Possiamo aggiornare una tabella alla volta per almeno 1 colonna o poche colonne.
  2. Abbiamo bisogno della clausola FROM per aggiungere un JOIN. L'oggetto nella clausola FROM può essere o meno lo stesso dell'oggetto da aggiornare.
  3. Possiamo utilizzare INNER o OUTER JOIN (vedi esempi più avanti).
  4. Possiamo aggiornare solo un sottoinsieme dei dati utilizzando la clausola WHERE.

Prima di avere i nostri esempi, prepariamo i dati.

I nostri dati di prova

Per l'amore per i film, creiamo un database di titoli di film con valutazioni degli utenti.

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

Ora che abbiamo il database e le tabelle, inseriamo alcuni dati:

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

AGGIORNAMENTO SQL Server con ISCRIVITI Esempio

Esamineremo diversi esempi aventi lo stesso obiettivo di aggiornare la OverallUserRating nei Titoli tavolo. Le valutazioni possono essere da 1 a 5. OverallUserRating è la media di tutte le valutazioni per il titolo di un film.

Ecco lo stato iniziale della tabella:

Esempio AGGIORNAMENTO A SINISTRA UNISCITI

-- 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 prima istruzione SELECT calcola la valutazione media per titolo del film in base a UserRatings tavolo. Il risultato viene scaricato in una tabella temporanea denominata #ComputedRatings . Poiché utilizziamo INNER JOIN, i titoli dei film senza valutazioni vengono scartati.

Nell'istruzione UPDATE, i Titoli la tabella è stata aggiornata utilizzando un LEFT JOIN da #ComputedRatings tavola temporanea. Se la valutazione media è null , il valore diventa Nessuna valutazione . Nel nostro esempio, Captain America:Civil War non ha ancora valutazioni degli utenti – vedi Figura 2.

Esempio SQL UPDATE INNER JOIN

Ecco come va:

-- 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

Quando esegui il codice precedente, il risultato sarà lo stesso della Figura 2. Ma qual è la differenza tra i due codici?

  • La prima istruzione SELECT considera la valutazione dell'utente NULL a differenza del nostro esempio LEFT JOIN precedente. Non elimina il titolo del film senza valutazioni degli utenti. Quindi, questa volta, il Nessuna valutazione valore per Captain America:Civil War è già considerato.
  • Un INNER JOIN con assegnazione diretta dell'AverageRating value è più appropriato poiché tutti i TitleID sono contabilizzati.

Ora, invece di una tabella temporanea, è possibile utilizzare anche un'espressione di tabella comune (CTE). Ecco il codice modificato:

-- 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

Maggiori informazioni

  • La tua guida definitiva a SQL JOIN:INNER JOIN – Parte 1
  • La tua guida definitiva a SQL JOIN:OUTER JOIN – Parte 2

Utilizzo dell'aggiornamento Comando con Unisciti In sicurezza (5 suggerimenti)

La sicurezza si riferisce all'aggiornamento dei record previsti. Inoltre, si tratta di NON toccare i record che non intendiamo aggiornare. Qui ci occuperemo di 5 scenari:

Visualizza prima i record con l'istruzione SELECT

Questo suggerimento è perfetto per alcuni record. Quindi, prima di modificare i record per l'aggiornamento, prova questo:

È facile da fare. E se sembra buono, decommenta le clausole UPDATE e SET. Contrassegna la clausola SELECT come commento. Allora sei a posto. In questo modo riduci al minimo il controllo dei danni grazie alla tua proattività.

Esegui una prova utilizzando le tabelle temporanee

Non sei sicuro se potrebbe verificarsi un errore? Quindi prova a scaricare i record della tabella che desideri aggiornare in una tabella temporanea. Dopodiché, esegui una corsa di prova da lì.

Non ci sono errori di runtime? I risultati sembrano buoni? Quindi, sostituisci la tabella temporanea con la tabella originale. In questo modo, sai che non ci saranno errori di runtime lungo il percorso.

Si noti inoltre che le tabelle temporanee sono uno dei modi per archiviare le copie delle tabelle originali. Puoi anche utilizzare tabelle con ottimizzazione per la memoria o un backup del database. Con i backup del database, hai più libertà di giocare con i record che devi aggiornare. Ma lo svantaggio di questo è lo spazio di archiviazione.

Maggiori informazioni

  • CREA TABELLA (Transact-SQL) – Tabelle temporanee

Prova ad aggiungere la clausola OUTPUT in UPDATE

Vuoi tornare indietro nel tempo prima di eseguire l'aggiornamento? Se sei così scettico, puoi usare la clausola OUTPUT e visualizzare il passato e il presente. Nell'esempio seguente, una variabile di tabella serve per eseguire il dump dei valori precedenti e presenti dopo l'aggiornamento. Puoi quindi SELEZIONARE la variabile della tabella per vedere i risultati:

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

Alcuni punti da notare su questo codice:

  • La variabile table funziona come un contenitore dei valori precedenti e presenti.
  • Il solito aggiornamento con CTE è in ordine. Utilizziamo ancora il tavolo temporaneo per giocare sul sicuro.
  • La clausola OUTPUT si applica per eseguire il dump dei valori precedenti e presenti nella variabile della tabella. INSERTED contiene nuovi valori, mentre DELETED ha vecchi valori.

Se emetti un SELECT dalla variabile della tabella, ecco cosa potresti aspettarti:

Il risultato è come la Figura 3, ma guarda al futuro. Questo guarda al passato. Se è diverso, qualcosa è andato storto nel mezzo.

La buona notizia è che puoi utilizzare i vecchi valori nella variabile table per ripristinarla allo stato precedente. Ma se è lo stesso, allora di nuovo, sei a posto. Puoi contrassegnare la clausola OUTPUT come commento o rimuoverla, quindi sostituire la tabella temporanea con la tabella originale.

Maggiori informazioni

  • Clausola OUTPUT (Transact-SQL)

Utilizza PROVA…CATCH per gestire gli errori futuri

I 3 suggerimenti precedenti sono utili durante la creazione e quindi il test del codice. Ma sappiamo tutti che non possiamo anticipare tutto. Pertanto, dobbiamo aggiungere più reti di sicurezza al codice.

Parlando di reti di sicurezza, T-SQL ha i blocchi di gestione degli errori TRY...CATCH come C# e C++.

Diamo un'occhiata al codice modificato dell'esempio precedente:

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

Ho modificato il codice sopra per forzare l'errore di troncamento della stringa. La valutazione complessiva degli utenti colonna nei Titoli la tabella può contenere solo fino a 10 caratteri. Nel CTE, l'abbiamo modificato in 20 caratteri. Non andrà bene. Il blocco CATCH prenderà il controllo del momento in cui si è verificato l'errore e fornirà le informazioni sull'errore.

Ecco il risultato:

Abbiamo attivato l'errore. Se devi rilevare errori imprevisti durante il runtime, questo è un modo per gestirlo.

Maggiori informazioni

  • PROVA…CATCH (Transact-SQL)

Utilizza la gestione delle transazioni

Infine, le transazioni. Garantisce di ripristinare tutto allo stato precedente prima che si verificasse l'errore, incluso UPDATE con JOIN e qualsiasi altra istruzione DML. Questa è una buona aggiunta al suggerimento n. 4 sopra.

Cambiamo nuovamente il codice per includere le transazioni:

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

È lo stesso dell'esempio precedente ad eccezione delle transazioni. Pertanto, forzerà un errore di troncamento della stringa. Non andrà oltre COMMIT TRANSACTION, ma piuttosto nel blocco CATCH con ROLLBACK TRANSACTION per ripristinare i valori allo stato precedente.

Questo è il modo in cui vogliamo giocare sul sicuro con aggiornamenti, inserimenti ed eliminazioni.

Nota :puoi progettare visivamente qualsiasi query in un diagramma utilizzando la funzionalità Generatore di query di dbForge Studio per SQL Server.

Maggiori informazioni

  • Best practice per T-SQL
  • Come scrivere query T-SQL come un professionista

Conclusione

Hai visto la sintassi di SQL UPDATE con JOIN. Esempi e 5 consigli pratici ti hanno ulteriormente illuminato. I requisiti possono differire da quelli presentati negli esempi, ma hai capito. Puoi ancora commettere errori. Tuttavia, è possibile ridurli quasi a zero.

Perché non applicare queste idee alla tua situazione?

Se questo post è stato utile, sentiti libero di spargere la voce sulle tue piattaforme di social media preferite. E se vuoi aggiungere delle ottime idee, sei il benvenuto nella sezione Commenti.