sql >> Databáze >  >> RDS >> Database

5 bezproblémových tipů pro použití příkazu SQL UPDATE s JOIN

„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ů.

  1. Můžeme aktualizovat jednu tabulku najednou pro alespoň 1 sloupec nebo několik sloupců.
  2. 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.
  3. Můžeme použít buď INNER nebo OUTER JOIN (viz příklady později).
  4. 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.


  1. MariaDB POWER() Vysvětleno

  2. Jak zřetězit text z více řádků do jednoho textového řetězce na serveru SQL Server

  3. Jak upravím sloupec MySQL tak, aby umožňoval NULL?

  4. Jak snadno nasadit TimescaleDB