„Ups! Mój błąd." Ile razy powiedziałeś to po tym, jak aktualizacja SQL UPDATE nie powiodła się? Chodzi o to, że jeśli nie będziesz ostrożny, aktualizacja tabeli może mieć poważne konsekwencje w postaci instrukcji DELETE. Może się jeszcze pogorszyć, jeśli skomplikujesz to, używając UPDATE z JOIN. Dlatego musisz to przemyśleć, zanim wciśniesz Execute lub wciśniesz CTRL-E.
Tak więc dzisiaj nauczysz się, jak bez kłopotów kodować SQL UPDATE za pomocą funkcji JOIN i nigdy nie mówić „Ups! Moje złe”.
Ale zanim zaczniemy ćwiczyć, zaczynamy od składni. Dzięki temu nasi nowicjusze poczują się jak w domu dzięki funkcji SQL Server UPDATE with JOIN. Następnie przygotujemy trochę danych i kilka przykładów. I na koniec zapoznaj się ze wskazówkami dotyczącymi bezpieczeństwa.
[sendpulse-form id=”12968″]
SQL ZAKTUALIZUJ składnię JOIN
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]
Musimy wyszczególnić kilka punktów z tego.
- Możemy aktualizować jedną tabelę na raz dla co najmniej 1 kolumny lub kilku kolumn.
- Potrzebujemy klauzuli FROM, aby dodać JOIN. Obiekt w klauzuli FROM może, ale nie musi być tym samym, co aktualizowany obiekt.
- Możemy użyć INNER lub OUTER JOIN (zobacz przykłady później).
- Możemy zaktualizować tylko podzbiór danych za pomocą klauzuli WHERE.
Zanim mamy nasze przykłady, przygotujmy dane.
Nasze dane testowe
Z miłości do filmów stwórzmy bazę danych tytułów filmów z ocenami użytkowników.
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
Teraz, gdy mamy już bazę danych i tabele, wstawmy trochę danych:
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
Aktualizacja serwera SQL za pomocą DOŁĄCZ Przykład
Zbadamy różne przykłady mające ten sam cel, jakim jest aktualizacja OverallUserRating w Tytułach stół. Oceny mogą wynosić od 1 do 5. Ogólna ocena użytkowników to średnia wszystkich ocen tytułu filmu.
Oto początkowy stan tabeli:
AKTUALIZACJA LEWEJ JOIN Przykład
-- 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
Pierwsze wyrażenie SELECT oblicza średnią ocenę tytułu filmu na podstawie Oceny użytkowników stół. Wynik jest zrzucany do tabeli tymczasowej o nazwie #ComputedRatings . Ponieważ używamy INNER JOIN, tytuły filmów bez ocen są odrzucane.
W instrukcji UPDATE Tytuły tabela została zaktualizowana za pomocą LEFT JOIN z #ComputedRatings tabela tymczasowa. Jeśli średnia ocena wynosi null , wartość zmieni się na Brak oceny . W naszym przykładzie Kapitan Ameryka:Wojna domowa nie ma jeszcze ocen użytkowników — patrz Rysunek 2.
Przykład SQL UPDATE INNER JOIN
Oto jak to działa:
-- 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
Po uruchomieniu powyższego kodu wynik będzie taki sam, jak na rysunku 2. Ale jaka jest różnica między tymi dwoma kodami?
- Pierwsza instrukcja SELECT uwzględnia ocenę użytkownika NULL, w przeciwieństwie do naszego wcześniejszego przykładu LEFT JOIN. Nie usuwa tytułu filmu bez ocen użytkownika. Tym razem więc Brak oceny wartość dla Kapitan Ameryka:Wojna domowa jest już rozważane.
- ZŁĄCZENIE WEWNĘTRZNE z bezpośrednim przypisaniem AverageRating wartość jest bardziej odpowiednia, ponieważ wszystkie TitleIDs są rozliczane.
Teraz zamiast tabeli tymczasowej można również użyć wspólnego wyrażenia tabelowego (CTE). Oto zmodyfikowany kod:
-- 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
Więcej informacji
- Twój ostateczny przewodnik po SQL JOIN:INNER JOIN – część 1
- Twój ostateczny przewodnik po SQL JOIN:OUTER JOIN – część 2
Korzystanie z aktualizacji Polecenie z Dołącz Bezpiecznie (5 wskazówek)
Bezpieczeństwo odnosi się do aktualizowania zamierzonych zapisów. Ponadto chodzi o NIE dotykanie rekordów, których nie zamierzamy aktualizować. Tutaj zajmiemy się 5 scenariuszami:
Najpierw wyświetl rekordy za pomocą instrukcji SELECT
Ta wskazówka jest idealna dla kilku płyt. Tak więc, zanim wpłyniesz na rekordy do aktualizacji, wypróbuj to:
To łatwe. A jeśli wygląda dobrze, odkomentuj klauzule UPDATE i SET. Oznacz klauzulę SELECT jako komentarz. Wtedy możesz już iść. W ten sposób minimalizujesz kontrolę nad uszkodzeniami dzięki swojej proaktywności.
Wykonaj próbę z użyciem tabel tymczasowych
Nie masz pewności, czy może wystąpić błąd? Następnie spróbuj zrzucić rekordy tabeli, które chcesz zaktualizować, do tabeli tymczasowej. Następnie wykonaj stamtąd próbę.
Czy nie ma błędów w czasie wykonywania? Czy wyniki wyglądają dobrze? Następnie zastąp tabelę tymczasową tabelą oryginalną. W ten sposób wiesz, że po drodze nie wystąpią żadne błędy wykonawcze.
Zauważ też, że tabele tymczasowe są jednym ze sposobów przechowywania kopii oryginalnych tabel. Możesz także użyć tabel zoptymalizowanych pod kątem pamięci lub kopii zapasowej bazy danych. Dzięki kopiom zapasowym bazy danych masz większą swobodę zabawy z rekordami, które musisz zaktualizować. Ale wadą tego jest miejsce do przechowywania.
Więcej informacji
- CREATE TABLE (Transact-SQL) — Tabele tymczasowe
Spróbuj dodać klauzulę OUTPUT do UPDATE
Czy chcesz cofnąć się w czasie przed uruchomieniem aktualizacji? Jeśli jesteś tak sceptyczny, możesz użyć klauzuli OUTPUT i spojrzeć na przeszłość i teraźniejszość. W poniższym przykładzie zmienna tabeli służy do zrzutu poprzednich i obecnych wartości po aktualizacji. Następnie możesz WYBRAĆ zmienną tabeli, aby zobaczyć wyniki:
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
Kilka punktów do zapamiętania na temat tego kodu:
- Zmienna table działa jako kontener poprzednich i obecnych wartości.
- Zwykła aktualizacja z CTE jest w porządku. Nadal używamy tymczasowego stołu, aby grać bezpiecznie.
- Klauzula OUTPUT ma zastosowanie do zrzucenia poprzedniej i obecnej wartości do zmiennej tabeli. INSERTED zawiera nowe wartości, podczas gdy DELETED ma stare.
Jeśli wydasz SELECT ze zmiennej tabeli, możesz się tego spodziewać:
Wynik jest podobny do rysunku 3, ale wygląda w przyszłość. Ten patrzy w przeszłość. Jeśli jest inaczej, coś poszło nie tak.
Dobrą wiadomością jest to, że możesz użyć starych wartości w zmiennej tabeli, aby przywrócić jej poprzedni stan. Ale jeśli tak jest, to znowu możesz iść. Możesz oznaczyć klauzulę OUTPUT jako komentarz lub ją usunąć, a następnie zastąpić tabelę tymczasową tabelą oryginalną.
Więcej informacji
- Klauzula WYJŚCIOWA (Transact-SQL)
Użyj TRY…CATCH do obsługi przyszłych błędów
Poprzednie 3 wskazówki są przydatne podczas tworzenia, a następnie testowania kodu. Ale wszyscy wiemy, że nie możemy wszystkiego przewidzieć. Dlatego musimy dodać więcej siatek bezpieczeństwa do kodu.
Mówiąc o sieciach bezpieczeństwa, T-SQL ma bloki obsługi błędów TRY…CATCH, takie jak C# i C++.
Rzućmy okiem na zmodyfikowany kod z poprzedniego przykładu:
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
Zmieniłem powyższy kod, aby wymusić błąd obcięcia ciągu. Ogólna ocena użytkowników kolumna w Tytułach tabela może pomieścić maksymalnie 10 znaków. W CTE zmieniliśmy go na 20 znaków. To nie pasuje. Blok CATCH przejmie moment wystąpienia błędu i dostarczy informacje o błędzie.
Oto wynik:
Wywołaliśmy błąd. Jeśli musisz wyłapać nieprzewidziane błędy w czasie wykonywania, jest to jeden ze sposobów na poradzenie sobie z tym.
Więcej informacji
- SPRÓBUJ…CATCH (Transact-SQL)
Użyj obsługi transakcji
Wreszcie transakcje. Zapewnia przywrócenie wszystkiego do poprzedniego stanu przed wystąpieniem błędu, w tym UPDATE with JOIN i innych instrukcji DML. To dobry dodatek do wskazówki nr 4 powyżej.
Zmieńmy kod ponownie, aby zawierał transakcje:
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
Jest tak samo jak w poprzednim przykładzie z wyjątkiem transakcji. W ten sposób wymusi błąd obcięcia ciągu. Nie przejdzie poza COMMIT TRANSACTION, ale raczej w bloku CATCH z ROLLBACK TRANSACTION, aby przywrócić wartości do ich poprzednich stanów.
To jest sposób, jeśli chcemy grać bezpiecznie z aktualizacjami, wstawkami i usuwaniem.
Uwaga :Możesz zaprojektować dowolne zapytanie wizualnie na diagramie za pomocą funkcji Query Builder w dbForge Studio dla SQL Server.
Więcej informacji
- Najlepsze praktyki T-SQL
- Jak pisać zapytania T-SQL jak profesjonalista
Wniosek
Widziałeś składnię SQL UPDATE z JOIN. Przykłady i 5 bezproblemowych wskazówek oświeciły Cię jeszcze bardziej. Wymagania mogą różnić się od przedstawionych na przykładach, ale masz rację. Nadal możesz popełniać błędy. Jednak możliwe jest zredukowanie ich do prawie zera.
Dlaczego nie zastosować tych pomysłów do swojej sytuacji?
Jeśli ten post był pomocny, możesz rozpowszechniać informacje na swoich ulubionych platformach społecznościowych. A jeśli chcesz dodać świetne pomysły, zapraszamy do sekcji Komentarze.