Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

5 mẹo không phiền phức để sử dụng câu lệnh SQL UPDATE với JOIN

"Giáo sư! Lỗi của tôi." Bạn đã nói điều này bao nhiêu lần sau khi CẬP NHẬT SQL bị lỗi? Vấn đề là, nếu bạn không cẩn thận, việc cập nhật bảng có thể gây ra hậu quả nghiêm trọng dưới dạng câu lệnh DELETE. Nó thậm chí có thể trở nên tồi tệ hơn nếu bạn làm phức tạp nó bằng cách sử dụng UPDATE với JOIN. Đó là lý do tại sao bạn cần suy nghĩ kỹ trước khi nhấn Execute hoặc nhấn CTRL-E.

Vì vậy, hôm nay bạn sẽ học cách viết mã CẬP NHẬT SQL của mình với JOIN mà không phức tạp và không bao giờ nói “Rất tiếc! My bad ”một lần nữa.

Nhưng trước khi bắt đầu thực hành, chúng ta bắt đầu với cú pháp. Nó cũng sẽ làm cho người mới của chúng tôi cảm thấy như ở nhà về CẬP NHẬT SQL Server với THAM GIA. Sau đó, chúng tôi sẽ chuẩn bị một số dữ liệu và một vài ví dụ. Và cuối cùng, hãy xem xét các mẹo an toàn.

[sendpulse-form id =”12968 ″]

SQL CẬP NHẬT Cú pháp THAM GIA

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]

Chúng tôi cần lặp lại một vài điểm từ điều này.

  1. Chúng tôi có thể cập nhật từng bảng một cho ít nhất 1 cột hoặc một vài cột.
  2. Chúng tôi cần mệnh đề FROM để thêm một JOIN. Đối tượng trong mệnh đề FROM có thể giống hoặc không giống với đối tượng đang được cập nhật.
  3. Chúng tôi có thể sử dụng INNER hoặc OUTER JOIN (xem ví dụ sau).
  4. Chúng tôi chỉ có thể cập nhật một tập hợp con dữ liệu bằng mệnh đề WHERE.

Trước khi có các ví dụ của mình, hãy chuẩn bị dữ liệu.

Dữ liệu thử nghiệm của chúng tôi

Đối với những người yêu thích phim, hãy tạo cơ sở dữ liệu tên phim với xếp hạng của người dùng.

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

Bây giờ chúng ta đã có cơ sở dữ liệu và bảng, hãy chèn một số dữ liệu:

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

CẬP NHẬT SQL Server với THAM GIA Ví dụ

Chúng tôi sẽ xem xét các ví dụ khác nhau có cùng mục tiêu cập nhật Đánh giá tổng thể người dùng trong Tiêu đề bàn. Xếp hạng có thể từ 1 đến 5. Đánh giá tổng thể của người dùng là mức trung bình của tất cả các xếp hạng cho một tựa phim.

Đây là trạng thái ban đầu của bảng:

Ví dụ CẬP NHẬT LEFT JOIN

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

Câu lệnh SELECT đầu tiên tính toán xếp hạng trung bình cho mỗi tiêu đề phim dựa trên Tỷ lệ người dùng bàn. Kết quả được đưa vào một bảng tạm thời có tên # ComputedRatings . Khi chúng tôi sử dụng INNER JOIN, các tựa phim không có xếp hạng sẽ bị loại bỏ.

Trong tuyên bố CẬP NHẬT, Tiêu đề bảng đã được cập nhật bằng cách sử dụng LEFT JOIN từ # ComputedRatings bảng tạm thời. Nếu xếp hạng trung bình là null , giá trị trở thành Không có xếp hạng . Trong mẫu của chúng tôi, Captain America:Civil War chưa có xếp hạng người dùng - xem Hình 2.

Ví dụ về SQL UPDATE INNER JOIN

Đây là cách nó diễn ra:

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

Khi bạn chạy đoạn mã trên, kết quả sẽ giống như trong Hình 2. Nhưng sự khác biệt giữa hai mã là gì?

  • Câu lệnh SELECT đầu tiên coi xếp hạng người dùng NULL không giống như ví dụ LEFT JOIN trước đó của chúng tôi. Nó không loại bỏ tiêu đề phim không có xếp hạng của người dùng. Vì vậy, lần này, Không có xếp hạng giá trị cho Captain America:Civil War được coi là đã.
  • THAM GIA INNER với sự chỉ định trực tiếp của Tỷ lệ trung bình giá trị phù hợp hơn vì tất cả TitleIDs được tính đến.

Bây giờ, thay vì một bảng tạm thời, biểu thức bảng chung (CTE) cũng có thể được sử dụng. Đây là mã đã sửa đổi:

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

Thông tin khác

  • Hướng dẫn cơ bản của bạn về SQL JOIN:INNER JOIN - Phần 1
  • Hướng dẫn cơ bản của bạn về SQL JOIN:OUTER JOIN - Phần 2

Sử dụng Cập nhật Lệnh với Tham gia An toàn (5 mẹo)

An toàn đề cập đến việc cập nhật các hồ sơ dự kiến. Ngoài ra, đó là việc KHÔNG chạm vào các bản ghi mà chúng tôi không có ý định cập nhật. Ở đây, chúng ta sẽ giải quyết 5 tình huống:

Xem bản ghi trước với câu lệnh SELECT

Mẹo này là hoàn hảo cho một số hồ sơ. Vì vậy, trước khi bạn ảnh hưởng đến các bản ghi để cập nhật, hãy thử điều này:

Điều đó thật dễ dàng . Và nếu nó có vẻ ổn, hãy bỏ ghi chú các mệnh đề CẬP NHẬT và ĐẶT. Đánh dấu mệnh đề SELECT làm bình luận. Sau đó, bạn tốt để đi. Bằng cách này bạn giảm thiểu việc kiểm soát thiệt hại vì chủ động.

Chạy thử bằng các bảng tạm thời

Bạn cảm thấy không chắc liệu có thể xảy ra lỗi không? Sau đó, cố gắng chuyển các bản ghi bảng mà bạn muốn cập nhật vào một bảng tạm thời. Sau đó, hãy chạy thử từ đó.

Không có lỗi thời gian chạy? Kết quả có tốt không? Sau đó, thay thế bảng tạm thời bằng bảng ban đầu. Bằng cách này, bạn biết rằng sẽ không có lỗi thời gian chạy trong quá trình thực hiện.

Ngoài ra, hãy lưu ý rằng các bảng tạm thời là một trong những cách để lưu trữ các bản sao của bảng gốc. Bạn cũng có thể sử dụng bảng được tối ưu hóa bộ nhớ hoặc sao lưu cơ sở dữ liệu. Với các bản sao lưu cơ sở dữ liệu, bạn có nhiều quyền tự do hơn để xử lý các bản ghi mà bạn cần cập nhật. Nhưng nhược điểm của điều này là không gian lưu trữ.

Thông tin khác

  • TẠO BẢNG (Transact-SQL) - Bảng Tạm thời

Cố gắng thêm mệnh đề OUTPUT vào UPDATE

Bạn có muốn quay lại thời gian trước khi chạy bản cập nhật không? Nếu bạn hoài nghi như vậy, bạn có thể sử dụng mệnh đề OUTPUT để xem quá khứ và hiện tại. Trong ví dụ bên dưới, một biến bảng dùng để kết xuất các giá trị trước đó và hiện tại sau khi cập nhật. Sau đó, bạn có thể CHỌN biến bảng để xem kết quả:

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

Một số điểm cần lưu ý về mã này:

  • Biến bảng hoạt động như một vùng chứa các giá trị trước đây và hiện tại.
  • Cập nhật thông thường với CTE là theo thứ tự. Chúng tôi vẫn sử dụng bàn chơi tạm thời để chơi an toàn.
  • Mệnh đề OUTPUT áp dụng để kết xuất các giá trị trước đó và hiện tại vào biến bảng. INSERTED chứa các giá trị mới, trong khi DELETED có các giá trị cũ.

Nếu bạn đưa ra một SELECT từ biến bảng, đây là những gì bạn có thể mong đợi:

Kết quả giống như Hình 3, nhưng nó nhìn vào tương lai. Điều này nhìn vào quá khứ. Nếu nó khác, có gì đó đã xảy ra ở giữa.

Tin tốt là bạn có thể sử dụng các giá trị cũ trong biến bảng để khôi phục nó về trạng thái trước đó. Nhưng nếu nó giống nhau, thì một lần nữa, bạn nên đi. Bạn có thể đánh dấu mệnh đề OUTPUT dưới dạng nhận xét hoặc xóa nó, sau đó thay thế bảng tạm thời bằng bảng ban đầu.

Thông tin khác

  • Mệnh đề OUTPUT (Giao dịch-SQL)

Sử dụng TRY… CATCH để xử lý các lỗi trong tương lai

3 mẹo trước rất hữu ích khi bạn đang tạo và sau đó kiểm tra mã của mình. Nhưng tất cả chúng ta đều biết rằng chúng ta không thể lường trước được mọi thứ. Do đó, chúng ta cần thêm nhiều lưới an toàn hơn vào mã.

Nói về mạng lưới an toàn, T-SQL có các khối xử lý lỗi TRY… CATCH như C # và C ++.

Hãy xem mã đã sửa đổi từ ví dụ trướ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

Tôi đã thay đổi mã ở trên để buộc lỗi cắt ngắn chuỗi. Đánh giá tổng thể của người dùng trong Tiêu đề bảng chỉ có thể chứa tối đa 10 ký tự. Trong CTE, chúng tôi đã thay đổi nó thành 20 ký tự. Điều đó sẽ không phù hợp. Khối CATCH sẽ tiếp nhận thời điểm xảy ra lỗi và cung cấp thông tin về lỗi.

Đây là kết quả:

Chúng tôi đã gây ra lỗi. Nếu bạn cần phát hiện các lỗi không lường trước được trong thời gian chạy, đây là một cách để xử lý.

Thông tin khác

  • THỬ… CATCH (Transact-SQL)

Sử dụng Xử lý Giao dịch

Cuối cùng là các giao dịch. Nó đảm bảo khôi phục mọi thứ về trạng thái trước đó trước khi xảy ra lỗi, bao gồm cả CẬP NHẬT với THAM GIA và bất kỳ câu lệnh DML nào khác. Đây là một bổ sung tốt cho Mẹo số 4 ở trên.

Hãy thay đổi mã một lần nữa để bao gồm các giao dịch:

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

Nó giống như trong ví dụ trước ngoại trừ các giao dịch. Vì vậy, nó sẽ buộc một lỗi cắt ngắn chuỗi. Nó sẽ không chuyển qua GIAO DỊCH COMMIT, mà là trong khối CATCH với GIAO DỊCH ROLLBACK để hoàn nguyên các giá trị về trạng thái trước đó của chúng.

Đây là cách nếu chúng ta muốn chơi an toàn với các bản cập nhật, chèn và xóa.

Lưu ý :Bạn có thể thiết kế bất kỳ truy vấn nào một cách trực quan trong một sơ đồ bằng cách sử dụng tính năng Trình tạo Truy vấn của dbForge Studio cho SQL Server.

Thông tin khác

  • Các phương pháp hay nhất về T-SQL
  • Cách viết các truy vấn T-SQL như một người chuyên nghiệp

Kết luận

Bạn đã thấy cú pháp của SQL UPDATE với JOIN. Ví dụ và 5 mẹo đơn giản giúp bạn hiểu thêm. Các yêu cầu có thể khác với những gì các ví dụ trình bày, nhưng bạn đã hiểu rõ. Bạn vẫn có thể mắc sai lầm. Tuy nhiên, có thể giảm chúng xuống gần bằng không.

Tại sao không áp dụng những ý tưởng này vào tình huống của bạn?

Nếu bài đăng này hữu ích, vui lòng phổ biến thông tin trên các nền tảng truyền thông xã hội yêu thích của bạn. Và nếu bạn muốn thêm một số ý tưởng tuyệt vời, bạn có thể tham gia phần Nhận xét.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thiếu chỉ mục trong MS SQL hoặc Tối ưu hóa nhanh chóng

  2. Làm thế nào để phân tích cú pháp chuỗi giống như một chuyên gia bằng cách sử dụng hàm SUBSTRING () của SQL?

  3. Cách trở thành nhà thiết kế cơ sở dữ liệu

  4. Percona DB là gì

  5. Khôi phục cơ sở dữ liệu DW mẫu AdventureWorksDW2019