Database
 sql >> база данни >  >> RDS >> Database

5 безпроблемни съвета за използване на SQL UPDATE изявление с JOIN

„Упс! Моя грешка." Колко пъти казахте това, след като SQL UPDATE се обърка? Работата е там, че ако не сте внимателни, актуализацията на таблицата може да има сериозни последици под формата на оператора DELETE. Може да стане още по-лошо, ако го усложните, като използвате UPDATE с JOIN. Ето защо трябва да помислите, преди да натиснете Execute или CTRL-E.

И така, днес ще научите как да кодирате своя SQL UPDATE с JOIN без проблеми и никога да не казвате „Ами! Моята лоша” отново.

Но преди да започнем да практикуваме, започваме със синтаксиса. Това също така ще накара нашите начинаещи да се чувстват като у дома си относно актуализацията на SQL Server с JOIN. След това ще подготвим някои данни и няколко примера. И накрая, разгледайте съветите за безопасност.

[sendpulse-form id=”12968″]

SQL Актуализиране на синтаксис 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]

Трябва да посочим няколко точки от това.

  1. Можем да актуализираме една таблица наведнъж за поне 1 колона или няколко колони.
  2. Нуждаем се от клаузата FROM, за да добавим JOIN. Обектът в клаузата FROM може или не може да бъде същият като обекта, който се актуализира.
  3. Можем да използваме INNER или OUTER JOIN (вижте примерите по-късно).
  4. Можем да актуализираме само подмножество от данни, използвайки клаузата WHERE.

Преди да имаме нашите примери, нека подготвим данните.

Нашите тестови данни

За любовта към филмите, нека създадем база данни със заглавия на филми с потребителски оценки.

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

Сега, когато имаме базата данни и таблиците, нека вмъкнем някои данни:

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 сървър с Присъединете се Пример

Ще разгледаме различни примери с една и съща цел за актуализиране на OverallUserRating в Заглавия маса. Оценките могат да бъдат от 1 до 5. OverallUserRating е средната стойност на всички оценки за заглавие на филм.

Ето първоначалното състояние на таблицата:

Пример за АКТУАЛИЗИРАНЕ НА ЛЯВО ПРИЕДИНЯВАНЕ

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

Първият оператор SELECT изчислява средната оценка за заглавие на филм въз основа на UserRatings маса. Резултатът се изхвърля във временна таблица, наречена #ComputedRatings . Тъй като използваме INNER JOIN, заглавията на филмите без оценки се отхвърлят.

В оператора UPDATE Заглавията таблицата беше актуализирана с помощта на LEFT JOIN от #ComputedRatings временна маса. Ако средната оценка е нула , стойността става Без оценка . В нашата извадка, Капитан Америка:Гражданска война все още няма потребителски оценки – вижте Фигура 2.

Пример за АКТУАЛИЗИРАНЕ на SQL INNER JOIN

Ето как става:

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

Когато изпълните горния код, резултатът ще бъде същият като на фигура 2. Но каква е разликата между двата кода?

  • Първият оператор SELECT взема предвид потребителската оценка NULL за разлика от нашия пример LEFT JOIN по-рано. Той не отхвърля заглавието на филма без потребителски оценки. И така, този път Без оценка стойност за Капитан Америка:Гражданска война вече се разглежда.
  • ВЪТРЕШНО ПРИСЪЕДИНЕНЕ с директно присвояване на Средна оценка стойността е по-подходяща, тъй като всички TitleID се отчитат.

Сега вместо временна таблица може да се използва и израз на обща таблица (CTE). Ето модифицирания код:

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

Повече информация

  • Вашето окончателно ръководство за SQL JOIN:INNER JOIN – част 1
  • Вашето окончателно ръководство за SQL JOIN:OUTER JOIN – част 2

Използване на актуализация Команда с присъединяване Безопасно (5 съвета)

Безопасността се отнася до актуализиране на предвидените записи. Освен това става дума за НЕ докосване на записите, които нямаме намерение да актуализираме. Тук ще разгледаме 5 сценария:

Вижте първо записите с оператора SELECT

Този съвет е идеален за няколко записа. Така че, преди да повлияете на записите за актуализиране, опитайте това:

Това е лесно за. И ако изглежда добре, разкоментирайте клаузите UPDATE и SET. Маркирайте клаузата SELECT като коментар. След това можете да тръгнете. По този начин намалявате до минимум контрола на щетите, тъй като сте проактивни.

Направете пробно изпълнение с помощта на временни таблици

Не сте сигурни дали може да възникне грешка? След това опитайте да изхвърлите записите на таблицата, които искате да актуализирате, във временна таблица. След това направете пробен пуск от там.

Няма ли грешки по време на изпълнение? Резултатите изглеждат ли добри? След това заменете временната таблица с оригиналната таблица. По този начин знаете, че няма да има грешки по време на изпълнение.

Също така имайте предвид, че временните таблици са един от начините за съхраняване на копията на оригиналните таблици. Можете също да използвате оптимизирани за паметта таблици или архивиране на база данни. С архивирането на базата данни имате повече свобода да играете със записи, които трябва да актуализирате. Но недостатъкът на това е място за съхранение.

Повече информация

  • СЪЗДАВАНЕ НА ТАБЛИЦА (Transact-SQL) – Временни таблици

Опитайте да добавите клаузата OUTPUT в UPDATE

Искате ли да се върнете назад във времето, преди да стартирате актуализацията? Ако сте толкова скептични, можете да използвате клаузата OUTPUT и да видите миналото и настоящето. В примера по-долу променлива в таблицата служи за изхвърляне на предишните и настоящите стойности след актуализацията. След това можете да ИЗБЕРЕТЕ променливата на таблицата, за да видите резултатите:

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

Няколко точки, които трябва да отбележите относно този код:

  • Таблицата променлива работи като контейнер на предишните и настоящите стойности.
  • Обикновената актуализация с CTE е наред. Все още използваме временната маса, за да играем на сигурно.
  • Клаузата OUTPUT се прилага за изхвърляне на предишните и настоящите стойности в променливата на таблицата. INSERTED съдържа нови стойности, докато DELETED има стари стойности.

Ако издадете SELECT от променливата на таблицата, ето какво може да очаквате:

Резултатът е като на фигура 3, но гледа в бъдещето. Този гледа в миналото. Ако е различно, нещо се е объркало между тях.

Добрата новина е, че можете да използвате стари стойности в променливата на таблицата, за да я възстановите до предишното й състояние. Но ако е същото, тогава отново можете да тръгнете. Можете да маркирате клаузата OUTPUT като коментар или да я премахнете и след това да замените временната таблица с оригиналната таблица.

Повече информация

  • Клауза OUTPUT (Transact-SQL)

Използвайте TRY…CATCH за обработка на бъдещи грешки

Предишните 3 съвета са полезни, когато изработвате и след това тествате кода си. Но всички знаем, че не можем да предвидим всичко. Следователно трябва да добавим още защитни мрежи към кода.

Говорейки за защитни мрежи, T-SQL има TRY…CATCH блокове за обработка на грешки като C# и C++.

Нека да разгледаме модифицирания код от предишния пример:

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

Промених кода по-горе, за да принудя грешката при съкращаване на низа. Общата потребителска оценка колона в Заглавия таблицата може да побере само до 10 знака. В CTE го променихме на 20 знака. Това няма да пасне. Блокът CATCH ще поеме момента на възникване на грешката и ще предостави информацията за грешката.

Ето резултата:

Ние задействахме грешката. Ако трябва да улавяте непредвидени грешки по време на изпълнение, това е един от начините да се справите.

Повече информация

  • ОПИТАЙТЕ...CATCH (Transact-SQL)

Използвайте обработка на транзакции

И накрая, транзакции. Той гарантира възстановяването на всичко до предишното му състояние преди да е възникнала грешката, включително UPDATE с JOIN и всякакви други DML изрази. Това е добро допълнение към Съвет №4 по-горе.

Нека отново променим кода, за да включим транзакции:

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

Това е същото като в предишния пример, с изключение на транзакциите. По този начин той ще принуди грешка при срязване на низ. Той няма да премине покрай COMMIT TRANSACTION, а по-скоро в блока CATCH с ROLLBACK TRANSACTION, за да върне стойностите към предишните им състояния.

Това е начинът, ако искаме да играем на сигурно с актуализации, вмъквания и изтривания.

Забележка :Можете да проектирате всяка заявка визуално в диаграма, като използвате функцията Query Builder на dbForge Studio за SQL Server.

Повече информация

  • Най-добри практики за T-SQL
  • Как да пишете T-SQL заявки като професионалист

Заключение

Видяхте синтаксиса на SQL UPDATE с JOIN. Примери и 5 безпроблемни съвета ви просветиха допълнително. Изискванията може да се различават от представените в примерите, но вие разбрахте същността. Все още можете да правите грешки. Възможно е обаче да ги намалите почти до нула.

Защо не приложите тези идеи към вашата ситуация?

Ако тази публикация е била полезна, не се колебайте да разпространите думата в любимите си социални медийни платформи. И ако искате да добавите някои страхотни идеи, добре дошли в секцията за коментари.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да създадете репликация на транзакции

  2. TCL команди в SQL

  3. Изследване на Java Unit Testing с JUnit Test Framework

  4. Десет често срещани заплахи за качеството на плана за изпълнение

  5. Блокчейн:какво е това, как работи и какво означава за големи данни