sql >> Database >  >> RDS >> Database

5 probleemloze tips om SQL UPDATE-instructie te gebruiken met JOIN

“Oeps! Mijn fout." Hoe vaak heb je dit gezegd nadat een SQL-UPDATE fout was gegaan? Het punt is dat als je niet oppast, een tabelupdate ernstige gevolgen kan hebben in de vorm van de DELETE-instructie. Het kan nog erger worden als je het ingewikkelder maakt door UPDATE met JOIN te gebruiken. Daarom moet je erover nadenken voordat je op Uitvoeren drukt of op CTRL-E drukt.

Dus vandaag leer je hoe je je SQL UPDATE codeert met JOIN zonder gedoe en zeg nooit "Oeps! Mijn fout".

Maar voordat we gaan oefenen, beginnen we met de syntaxis. Het zorgt er ook voor dat onze nieuwelingen zich thuis voelen over SQL Server UPDATE met JOIN. Vervolgens zullen we enkele gegevens en enkele voorbeelden voorbereiden. En tot slot, bekijk de veiligheidstips.

[sendpulse-form id=”12968″]

SQL UPDATE JOIN-syntaxis

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]

Hiervan moeten we een paar punten specificeren.

  1. We kunnen één tabel tegelijk bijwerken voor ten minste één kolom of een paar kolommen.
  2. We hebben de FROM-component nodig om een ​​JOIN toe te voegen. Het object in de FROM-component kan al dan niet hetzelfde zijn als het object dat wordt bijgewerkt.
  3. We kunnen INNER of OUTER JOIN gebruiken (zie voorbeelden later).
  4. We kunnen alleen een subset van de gegevens bijwerken met behulp van de WHERE-clausule.

Voordat we onze voorbeelden hebben, laten we de gegevens voorbereiden.

Onze testgegevens

Laten we uit liefde voor films een database maken met filmtitels met gebruikersbeoordelingen.

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

Nu we de database en tabellen hebben, laten we wat gegevens invoegen:

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

SQL Server-UPDATE met DOE MEE Voorbeeld

We zullen verschillende voorbeelden onderzoeken met hetzelfde doel, namelijk het bijwerken van de OverallUserRating in de Titels tafel. Beoordelingen kunnen van 1 tot 5 zijn. OverallUserRating is het gemiddelde van alle beoordelingen voor een filmtitel.

Dit is de beginstatus van de tabel:

UPDATE LEFT JOIN Voorbeeld

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

De eerste SELECT-instructie berekent de gemiddelde beoordeling per filmtitel op basis van de UserRatings tafel. Het resultaat wordt gedumpt in een tijdelijke tabel met de naam #ComputedRatings . Omdat we INNER JOIN gebruiken, worden de filmtitels zonder beoordelingen verwijderd.

In de UPDATE-instructie, de Titels tabel is bijgewerkt met een LEFT JOIN van de #ComputedRatings tijdelijke tafel. Als de gemiddelde beoordeling null is , wordt de waarde Geen beoordeling . In ons voorbeeld, Captain America:Civil War heeft nog geen gebruikersbeoordelingen – zie figuur 2.

SQL UPDATE INNER JOIN Voorbeeld

Zo gaat het:

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

Wanneer u de bovenstaande code uitvoert, is het resultaat hetzelfde als in afbeelding 2. Maar wat is het verschil tussen de twee codes?

  • De eerste SELECT-instructie beschouwt de NULL-gebruikersbeoordeling in tegenstelling tot ons LEFT JOIN-voorbeeld eerder. Het verwijdert de filmtitel niet zonder gebruikersbeoordelingen. Dus deze keer de Geen beoordeling waarde voor Captain America:Civil War wordt al overwogen.
  • Een INNER JOIN met de directe toewijzing van de AverageRating waarde is geschikter omdat alle TitleID's worden verantwoord.

In plaats van een tijdelijke tabel kan nu ook de Common Table Expression (CTE) worden gebruikt. Hier is de aangepaste code:

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

Meer informatie

  • Uw ultieme gids voor SQL JOIN:INNER JOIN – Deel 1
  • Uw ultieme gids voor SQL JOIN:OUTER JOIN – Deel 2

Update gebruiken Opdracht met Join Veilig (5 tips)

Veiligheid verwijst naar het bijwerken van de beoogde records. Het gaat er ook om de records NIET aan te raken die we niet willen bijwerken. Hier behandelen we 5 scenario's:

Bekijk eerst de records met de SELECT-instructie

Deze tip is perfect voor een paar platen. Dus, voordat u de records voor het bijwerken beïnvloedt, probeert u dit:

Dat is gemakkelijk te doen. En als het er goed uitziet, verwijder dan de opmerkingen over de UPDATE- en SET-clausules. Markeer de SELECT-component als een opmerking. Dan ben je klaar om te gaan. Op deze manier minimaliseert u schadebeheersing door proactief te zijn.

Doe een proefrun met tijdelijke tabellen

Twijfel je of er een fout kan optreden? Probeer vervolgens de tabelrecords die u wilt bijwerken naar een tijdelijke tabel te dumpen. Voer daarna een proefrun uit.

Zijn er geen runtime-fouten? Zien de resultaten er goed uit? Vervang vervolgens de tijdelijke tabel door de originele tabel. Op deze manier weet u dat er onderweg geen runtime-fouten optreden.

Merk ook op dat tijdelijke tabellen een van de manieren zijn om kopieën van originele tabellen op te slaan. U kunt ook voor geheugen geoptimaliseerde tabellen of een databaseback-up gebruiken. Met de databaseback-ups heb je meer vrijheid om te spelen met records die je moet bijwerken. Maar het nadeel hiervan is opslagruimte.

Meer informatie

  • TABEL MAKEN (Transact-SQL) - Tijdelijke tabellen

Probeer de OUTPUT-clausule toe te voegen aan UPDATE

Wil je terug in de tijd voordat je de update uitvoert? Als je zo sceptisch bent, kun je de OUTPUT-clausule gebruiken en het verleden en het heden bekijken. In het onderstaande voorbeeld dient een tabelvariabele om de vorige en huidige waarden na de update te dumpen. U kunt dan de tabelvariabele SELECTEREN om de resultaten te zien:

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

Een paar opmerkingen over deze code:

  • De tabelvariabele werkt als een container van de vorige en huidige waarden.
  • De gebruikelijke update met CTE is in orde. We gebruiken de tijdelijke tafel nog steeds om op veilig te spelen.
  • De OUTPUT-component is van toepassing om de vorige en huidige waarden in de tabelvariabele te dumpen. INSERTED bevat nieuwe waarden, terwijl DELETED oude waarden heeft.

Als u een SELECT uit de tabelvariabele afgeeft, kunt u het volgende verwachten:

Het resultaat is zoals in figuur 3, maar het kijkt in de toekomst. Deze kijkt in het verleden. Als het anders is, is er tussendoor iets misgegaan.

Het goede nieuws is dat u oude waarden in de tabelvariabele kunt gebruiken om deze in de vorige staat te herstellen. Maar als het hetzelfde is, dan ben je weer klaar om te gaan. U kunt de OUTPUT-component markeren als een opmerking of deze verwijderen en vervolgens de tijdelijke tabel vervangen door de originele tabel.

Meer informatie

  • OUTPUT-clausule (Transact-SQL)

Gebruik TRY...CATCH om toekomstige fouten op te lossen

De vorige 3 tips zijn handig bij het maken en testen van uw code. Maar we weten allemaal dat we niet alles kunnen voorzien. We moeten dus meer vangnetten aan de code toevoegen.

Over vangnetten gesproken, T-SQL heeft de TRY...CATCH-foutafhandelingsblokken zoals C# en C++.

Laten we eens kijken naar de gewijzigde code uit het vorige voorbeeld:

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

Ik heb de bovenstaande code gewijzigd om de fout bij het afbreken van de tekenreeks te forceren. De OverallUserRating kolom in de Titels tabel is geschikt voor maximaal 10 tekens. In de CTE hebben we dit gewijzigd in 20 tekens. Dat past niet. Het CATCH-blok neemt het moment van het optreden van de fout over en geeft de foutinformatie.

Dit is het resultaat:

We hebben de fout geactiveerd. Als u tijdens runtime onvoorziene fouten moet opvangen, is dit een manier om dit aan te pakken.

Meer informatie

  • PROBEER…CATCH (Transact-SQL)

Gebruik transactieverwerking

Tot slot transacties. Het zorgt ervoor dat alles in de vorige staat wordt hersteld voordat de fout optrad, inclusief UPDATE met JOIN en andere DML-instructies. Dit is een goede aanvulling op Tip #4 hierboven.

Laten we de code opnieuw wijzigen om transacties op te nemen:

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

Het is hetzelfde als in het vorige voorbeeld, behalve voor transacties. Het zal dus een fout bij het afbreken van een string forceren. Het gaat niet verder dan COMMIT TRANSACTION, maar eerder in het CATCH-blok met de ROLLBACK TRANSACTION om de waarden terug te zetten naar hun vorige statussen.

Dit is de manier als we op veilig willen spelen met updates, invoegingen en verwijderingen.

Opmerking :U kunt elke query visueel in een diagram ontwerpen met de functie Query Builder van dbForge Studio voor SQL Server.

Meer informatie

  • T-SQL-best practices
  • T-SQL-query's schrijven als een professional

Conclusie

Je hebt de syntaxis van SQL UPDATE gezien met JOIN. Voorbeelden en 5 probleemloze tips brachten je verder. De vereisten kunnen verschillen van wat de voorbeelden presenteren, maar u begrijpt het punt. Je kunt nog steeds fouten maken. Het is echter mogelijk om ze terug te brengen tot bijna nul.

Waarom pas je deze ideeën niet toe op jouw situatie?

Als dit bericht nuttig was, voel je dan vrij om het te verspreiden op je favoriete sociale-mediaplatforms. En als je geweldige ideeën wilt toevoegen, ben je welkom in het gedeelte Opmerkingen.


  1. Werk rijen in de ene tabel bij met gegevens uit een andere tabel op basis van een kolom in elke tabel die gelijk is

  2. Het relationele model

  3. Voordelen en nadelen van het gebruik van opgeslagen procedures

  4. Paging implementeren met behulp van OFFSET FETCH NEXT in SQL Server