„Jejda! Moje chyba." Kolikrát jste to řekli poté, co se AKTUALIZACE SQL pokazila? Jde o to, že pokud si nedáte pozor, aktualizace tabulky může mít vážné důsledky ve formě příkazu DELETE. Mohlo by to být ještě horší, pokud to zkomplikujete pomocí UPDATE s JOIN. Proto si to musíte dobře promyslet, než stisknete Execute nebo stisknete CTRL-E.
Dnes se tedy naučíte, jak kódovat svůj SQL UPDATE pomocí JOIN bez potíží a nikdy neříkat „Jejda! Můj špatný“ znovu.
Než se ale dostaneme k praxi, začneme se syntaxí. Díky tomu se naši nováčci budou cítit jako doma, pokud jde o SQL Server UPDATE s JOIN. Poté připravíme data a několik příkladů. A nakonec prozkoumejte bezpečnostní tipy.
[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]
Musíme z toho vyjmenovat několik bodů.
- Můžeme aktualizovat jednu tabulku najednou pro alespoň 1 sloupec nebo několik sloupců.
- K přidání JOIN potřebujeme klauzuli FROM. Objekt v klauzuli FROM může nebo nemusí být stejný jako objekt, který je aktualizován.
- Můžeme použít buď INNER nebo OUTER JOIN (viz příklady později).
- Pomocí klauzule WHERE můžeme aktualizovat pouze podmnožinu dat.
Než budeme mít příklady, připravme si data.
Naše testovací údaje
Pro lásku k filmům, pojďme vytvořit databázi filmových titulů s uživatelskými hodnoceními.
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
Nyní, když máme databázi a tabulky, vložíme nějaká 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
AKTUALIZACE SQL Serveru pomocí PŘIPOJIT SE Příklad
Prozkoumáme různé příklady se stejným cílem aktualizace OverallUserRating v části Názvy stůl. Hodnocení může být 1 až 5. OverallUserRating je průměr všech hodnocení pro filmový titul.
Zde je počáteční stav tabulky:
UPDATE LEFT JOIN Příklad
-- 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
První příkaz SELECT vypočítá průměrné hodnocení na filmový titul na základě UserRatings stůl. Výsledek je uložen do dočasné tabulky s názvem #ComputedRatings . Jak používáme INNER JOIN, názvy filmů bez hodnocení jsou vyřazeny.
V příkazu UPDATE Tituly tabulka byla aktualizována pomocí LEFT JOIN z #ComputedRatings dočasný stůl. Pokud je průměrné hodnocení null , hodnota se změní na Žádné hodnocení . V naší ukázce Captain America:Civil War zatím nemá žádná uživatelská hodnocení – viz obrázek 2.
Příklad SQL UPDATE INNER JOIN
Jde to takto:
-- 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
Když spustíte výše uvedený kód, výsledek bude stejný jako na obrázku 2. Jaký je ale mezi těmito dvěma kódy rozdíl?
- První příkaz SELECT zohledňuje uživatelské hodnocení NULL na rozdíl od našeho předchozího příkladu LEFT JOIN. Nezahazuje název filmu bez uživatelského hodnocení. Tentokrát tedy Žádné hodnocení hodnota pro Captain America:Civil War se již uvažuje.
- INNER JOIN s přímým přiřazením AverageRating hodnota je vhodnější, protože všechna ID názvu jsou zaúčtovány.
Nyní lze místo dočasné tabulky použít také společný tabulkový výraz (CTE). Zde je upravený kód:
-- 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
Další informace
- Váš dokonalý průvodce SQL JOIN:INNER JOIN – část 1
- Váš dokonalý průvodce SQL JOIN:OUTER JOIN – část 2
Pomocí aktualizace Příkaz pomocí Připojit Bezpečně (5 tipů)
Bezpečnost se týká aktualizace zamýšlených záznamů. Jde také o NEDOTÝKÁNÍ se záznamů, které nemáme v úmyslu aktualizovat. Zde se budeme zabývat 5 scénáři:
Nejdříve si prohlédněte záznamy pomocí příkazu SELECT
Tento tip je ideální pro několik záznamů. Než tedy ovlivníte aktualizaci záznamů, zkuste toto:
To je snadné. A pokud to vypadá dobře, odkomentujte klauzule UPDATE a SET. Označte klauzuli SELECT jako komentář. Pak můžete jít. Tímto způsobem minimalizujete kontrolu poškození, protože jste proaktivní.
Proveďte zkušební provoz pomocí dočasných tabulek
Nejste si jisti, zda může dojít k chybě? Potom zkuste vypsat záznamy tabulky, které chcete aktualizovat, do dočasné tabulky. Poté proveďte zkušební provoz odtud.
Nevyskytují se žádné runtime chyby? Vypadají výsledky dobře? Potom nahraďte dočasnou tabulku původní tabulkou. Tímto způsobem budete vědět, že během běhu nedojde k žádným chybám za běhu.
Všimněte si také, že dočasné tabulky jsou jedním ze způsobů, jak uložit kopie původních tabulek. Můžete také použít tabulky optimalizované pro paměť nebo zálohu databáze. Díky zálohám databáze máte větší svobodu hrát si se záznamy, které potřebujete aktualizovat. Nevýhodou je ale úložný prostor.
Další informace
- CREATE TABLE (Transact-SQL) – dočasné tabulky
Zkuste přidat klauzuli OUTPUT do UPDATE
Chcete se vrátit v čase před spuštěním aktualizace? Pokud jste tak skeptičtí, můžete použít klauzuli OUTPUT a podívat se na minulost a současnost. V níže uvedeném příkladu slouží proměnná tabulky k výpisu předchozích a současných hodnot po aktualizaci. Poté můžete VYBRAT proměnnou tabulky a zobrazit výsledky:
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ěkolik poznámek k tomuto kódu:
- Proměnná tabulka funguje jako kontejner předchozích a současných hodnot.
- Obvyklá aktualizace pomocí CTE je v pořádku. Stále používáme dočasný stůl, abychom hráli na jistotu.
- Klauzule OUTPUT se vztahuje na výpis předchozích a současných hodnot do proměnné tabulky. INSERTED obsahuje nové hodnoty, zatímco DELETED má staré hodnoty.
Pokud zadáte SELECT z proměnné tabulky, můžete očekávat následující:
Výsledek je jako na obrázku 3, ale dívá se do budoucnosti. Tenhle se dívá do minulosti. Pokud je to jiné, něco se mezi tím pokazilo.
Dobrou zprávou je, že můžete použít staré hodnoty v proměnné tabulky a obnovit ji do předchozího stavu. Ale pokud je to stejné, pak znovu, můžete jít. Klauzuli OUTPUT můžete označit jako komentář nebo ji odstranit a poté nahradit dočasnou tabulku původní tabulkou.
Další informace
- Klauzule OUTPUT (Transact-SQL)
K řešení budoucích chyb použijte TRY…CATCH
Předchozí 3 tipy jsou užitečné při vytváření a následném testování kódu. Ale všichni víme, že nemůžeme předvídat všechno. Proto musíme do kódu přidat další bezpečnostní sítě.
Když už mluvíme o bezpečnostních sítích, T-SQL má bloky pro zpracování chyb TRY…CATCH jako C# a C++.
Podívejme se na upravený kód z předchozího příkladu:
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
Změnil jsem výše uvedený kód, abych vynutil chybu zkrácení řetězce. OverallUserRating ve sloupci Tituly tabulka může obsahovat pouze 10 znaků. V CTE jsme to změnili na 20 znaků. To se nehodí. Blok CATCH převezme okamžik výskytu chyby a poskytne informaci o chybě.
Zde je výsledek:
Spustili jsme chybu. Pokud potřebujete zachytit nepředvídané chyby během běhu, toto je jeden způsob, jak to zvládnout.
Další informace
- ZKUSTE…CATCH (Transact-SQL)
Použijte zpracování transakcí
Konečně transakce. Zajišťuje obnovení všeho do předchozího stavu, než došlo k chybě, včetně UPDATE pomocí JOIN a jakýchkoli dalších příkazů DML. Toto je dobrý doplněk k tipu č. 4 výše.
Změňme kód znovu tak, aby zahrnoval transakce:
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
Je to stejné jako v předchozím příkladu s výjimkou transakcí. Vynutí si tedy chybu zkrácení řetězce. Nepřejde přes COMMIT TRANSACTION, ale spíše v bloku CATCH s ROLLBACK TRANSACTION, aby se hodnoty vrátily do jejich předchozího stavu.
Toto je způsob, pokud chceme hrát na jistotu s aktualizacemi, vkládáním a mazáním.
Poznámka :Jakýkoli dotaz můžete navrhnout vizuálně v diagramu pomocí funkce Query Builder v dbForge Studio pro SQL Server.
Další informace
- Osvědčené postupy T-SQL
- Jak psát dotazy T-SQL jako profesionál
Závěr
Viděli jste syntaxi SQL UPDATE s JOIN. Příklady a 5 bezproblémových tipů vás osvětlilo dále. Požadavky se mohou lišit od toho, co představují příklady, ale rozumíte tomu. Stále můžete dělat chyby. Je však možné je snížit téměř na nulu.
Proč tyto myšlenky nevztáhnout na svou situaci?
Pokud byl tento příspěvek užitečný, neváhejte ho šířit na svých oblíbených platformách sociálních médií. A pokud chcete přidat nějaké skvělé nápady, jste vítáni v sekci Komentáře.