sql >> Databasteknik >  >> RDS >> Database

5 problemfria tips för att använda SQL UPDATE Statement med JOIN

"Hoppsan! Mitt fel." Hur många gånger sa du detta efter att en SQL-UPPDATERING hade blivit fel? Saken är den att om du inte är försiktig kan en tabelluppdatering få allvarliga konsekvenser i form av DELETE-satsen. Det kan bli ännu värre om du komplicerar det genom att använda UPDATE med JOIN. Det är därför du måste tänka över det innan du trycker på Execute eller trycker på CTRL-E.

Så idag kommer du att lära dig hur du kodar din SQL-UPPDATERING med JOIN utan krångel och aldrig säga "Hoppsan! My bad” igen.

Men innan vi börjar träna börjar vi med syntaxen. Det kommer också att få våra nybörjare att känna sig hemma när det gäller SQL Server UPDATE med JOIN. Sedan kommer vi att förbereda lite data och några exempel. Och slutligen, undersök säkerhetstipsen.

[sendpulse-form id="12968″]

SQL UPPDATERA JOIN-syntax

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]

Vi måste specificera några punkter från detta.

  1. Vi kan uppdatera en tabell i taget för minst en kolumn eller några kolumner.
  2. Vi behöver FROM-satsen för att lägga till en JOIN. Objektet i FROM-satsen kan vara detsamma som objektet som uppdateras.
  3. Vi kan använda antingen INNER eller OUTER JOIN (se exempel senare).
  4. Vi kan bara uppdatera en delmängd av data med hjälp av WHERE-satsen.

Innan vi har våra exempel, låt oss förbereda data.

Vår testdata

För kärleken till filmer, låt oss skapa en databas med filmtitlar med användarbetyg.

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 när vi har databasen och tabellerna, låt oss infoga lite data:

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 UPPDATERING med GÅ MED Exempel

Vi kommer att undersöka olika exempel som har samma mål att uppdatera OverallUserRating i Titlar tabell. Betygen kan vara 1 till 5. OverallUserRating är genomsnittet av alla betyg för en filmtitel.

Här är utgångsläget för tabellen:

UPDATERA VÄNSTER JOIN Exempel

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

Den första SELECT-satsen beräknar det genomsnittliga betyget per filmtitel baserat på UserRatings tabell. Resultatet dumpas till en tillfällig tabell som heter #ComputedRatings . Eftersom vi använder INNER JOIN slängs filmtitlarna utan betyg.

I UPDATE-satsen, Titlar tabellen uppdaterades med en LEFT JOIN från #ComputedRatings tillfälligt bord. Om det genomsnittliga betyget är null , värdet blir Inget betyg . I vårt exempel, Captain America:Civil War har inga användarbetyg än – se figur 2.

SQL UPPDATERING INNER JOIN Exempel

Så här går det till:

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

När du kör ovanstående kod blir resultatet detsamma som i figur 2. Men vad är skillnaden mellan de två koderna?

  • Den första SELECT-satsen tar hänsyn till användarbetyget NULL till skillnad från vårt LEFT JOIN-exempel tidigare. Den kasserar inte filmtiteln utan användarbetyg. Så den här gången, Inget betyg värde för Captain America:Civil War övervägs redan.
  • En INNER JOIN med direkt tilldelning av AverageRating värde är mer lämpligt eftersom alla TitleIDs redovisas.

Nu, istället för en temporär tabell, kan också Common table expression (CTE) användas. Här är den ändrade koden:

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

Mer information

  • Din ultimata guide till SQL JOIN:INNER JOIN – Del 1
  • Din ultimata guide till SQL JOIN:OUTER JOIN – Del 2

Använda uppdatering Kommando med Join Säkert (5 tips)

Säkerhet avser att uppdatera avsedda register. Det handlar också om att INTE röra de poster som vi inte har för avsikt att uppdatera. Här kommer vi att ta itu med fem scenarier:

Visa posterna först med SELECT-satsen

Det här tipset är perfekt för några skivor. Så, innan du påverkar posterna för uppdatering, prova detta:

Det är lätt att göra. Och om det ser bra ut, avkommentera UPDATE- och SET-klausulerna. Markera SELECT-satsen som en kommentar. Då är du bra att gå. På så sätt minimerar du skadekontroll eftersom du är proaktiv.

Gör en provkörning med tillfälliga tabeller

Känner du dig osäker på om ett fel kan uppstå? Försök sedan att dumpa tabellposterna du vill uppdatera till en tillfällig tabell. Efter det, gör en provkörning därifrån.

Finns det inga körtidsfel? Ser resultaten bra ut? Byt sedan ut den tillfälliga tabellen med den ursprungliga tabellen. På så sätt vet du att det inte kommer att finnas några körtidsfel på vägen.

Observera också att tillfälliga tabeller är ett av sätten att lagra kopior av originaltabeller. Du kan också använda minnesoptimerade tabeller eller en databassäkerhetskopiering. Med databassäkerhetskopiorna har du större frihet att leka med skivor du behöver uppdatera. Men nackdelen med detta är lagringsutrymme.

Mer information

  • SKAPA TABELL (Transact-SQL) – Tillfälliga tabeller

Försök att lägga till OUTPUT-klausulen i UPDATE

Vill du komma tillbaka i tiden innan du kör uppdateringen? Om du är så skeptisk kan du använda OUTPUT-klausulen och se det förflutna och nuet. I exemplet nedan tjänar en tabellvariabel till att dumpa tidigare och nuvarande värden efter uppdateringen. Du kan sedan VÄLJA tabellvariabeln för att se resultaten:

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

Några punkter att notera om den här koden:

  • Tabellvariabeln fungerar som en behållare för föregående och nuvarande värden.
  • Den vanliga uppdateringen med CTE är i sin ordning. Vi använder fortfarande det tillfälliga bordet för att spela säkert.
  • OUTPUT-satsen gäller för att dumpa föregående och nuvarande värden i tabellvariabeln. INSERTED innehåller nya värden, medan DELETED har gamla värden.

Om du utfärdar en SELECT från tabellvariabeln kan du förvänta dig det här:

Resultatet är som i figur 3, men det ser in i framtiden. Den här ser in i det förflutna. Om det är annorlunda har något gått fel däremellan.

Den goda nyheten är att du kan använda gamla värden i tabellvariabeln för att återställa den till dess tidigare tillstånd. Men om det är samma sak, då är du bra att gå. Du kan markera OUTPUT-satsen som en kommentar eller ta bort den och sedan ersätta den tillfälliga tabellen med den ursprungliga tabellen.

Mer information

  • OUTPUT-klausul (Transact-SQL)

Använd TRY…CATCH för att hantera framtida fel

De tre föregående tipsen är användbara när du skapar och sedan testar din kod. Men vi vet alla att vi inte kan förutse allt. Därför måste vi lägga till fler skyddsnät i koden.

På tal om skyddsnät, T-SQL har TRY…CATCH-felhanteringsblocken som C# och C++.

Låt oss ta en titt på den modifierade koden från föregående exempel:

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

Jag ändrade koden ovan för att tvinga fram strängavkortningsfelet. OverallUserRating kolumnen i Titlar Tabellen kan endast rymma upp till 10 tecken. I CTE ändrade vi det till 20 tecken. Det kommer inte att passa. CATCH-blocket tar över ögonblicket då felet inträffade och ger felinformationen.

Här är resultatet:

Vi utlöste felet. Om du behöver fånga oförutsedda fel under körning är detta ett sätt att hantera det.

Mer information

  • TRY...CATCH (Transact-SQL)

Använd transaktionshantering

Slutligen, transaktioner. Det säkerställer att allt återställs till sitt tidigare tillstånd innan felet inträffade, inklusive UPDATE med JOIN och andra DML-satser. Detta är ett bra tillägg till tips #4 ovan.

Låt oss ändra koden igen för att inkludera transaktioner:

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

Det är samma som i föregående exempel förutom transaktioner. Således kommer det att tvinga fram ett strängavkortningsfel. Det går inte förbi COMMIT TRANSACTION, utan snarare i CATCH-blocket med ROLLBACK TRANSACTION för att återställa värdena till deras tidigare tillstånd.

Detta är sättet om vi vill spela säkert med uppdateringar, infogar och raderingar.

Obs :Du kan designa vilken fråga som helst visuellt i ett diagram med hjälp av Query Builder-funktionen i dbForge Studio för SQL Server.

Mer information

  • Bästa metoder för T-SQL
  • Hur man skriver T-SQL-frågor som ett proffs

Slutsats

Du har sett syntaxen för SQL UPDATE med JOIN. Exempel och 5 problemfria tips upplyste dig ytterligare. Kraven kan skilja sig från vad exemplen presenterar, men du fattade poängen. Du kan fortfarande göra misstag. Det är dock möjligt att minska dem till nära noll.

Varför inte tillämpa dessa idéer på din situation?

Om det här inlägget var till hjälp, sprid gärna ordet på dina favoritplattformar för sociala medier. Och om du vill lägga till några bra idéer är du välkommen till kommentarsektionen.


  1. Databasmodell för ett meddelandesystem

  2. Oracle datumskillnad för att få antal år

  3. PHP - Använder PDO med IN-klausul array

  4. Vilka är fördelarna med att ha en försäljningsdatabas?